def test_secret_key_changes(self): """Test that a new anonymous id is returned when the secret key changes.""" CourseEnrollment.enroll(self.user, self.course.id) anonymous_id = anonymous_id_for_user(self.user, self.course.id) with override_settings(SECRET_KEY='some_new_and_totally_secret_key'): # Recreate user object to clear cached anonymous id. self.user = User.objects.get(pk=self.user.id) new_anonymous_id = anonymous_id_for_user(self.user, self.course.id) self.assertNotEqual(anonymous_id, new_anonymous_id) self.assertEqual(self.user, user_by_anonymous_id(anonymous_id)) self.assertEqual(self.user, user_by_anonymous_id(new_anonymous_id))
def submissions_score_set_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_set signal defined in the Submissions API, and convert it to a SCORE_CHANGED signal defined in this module. Converts the unicode keys for user, course and item into the standard representation for the SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_set): - 'points_possible': integer, - 'points_earned': integer, - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ points_possible = kwargs['points_possible'] points_earned = kwargs['points_earned'] course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) # If any of the kwargs were missing, at least one of the following values # will be None. SCORE_CHANGED.send(sender=None, points_possible=points_possible, points_earned=points_earned, user=user, course_id=course_id, usage_id=usage_id)
def submissions_score_set_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_set signal defined in the Submissions API, and convert it to a PROBLEM_SCORE_CHANGED signal defined in this module. Converts the unicode keys for user, course and item into the standard representation for the PROBLEM_SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_set): - 'points_possible': integer, - 'points_earned': integer, - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ points_possible = kwargs['points_possible'] points_earned = kwargs['points_earned'] course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) if user is None: return PROBLEM_SCORE_CHANGED.send( sender=None, points_earned=points_earned, points_possible=points_possible, user_id=user.id, course_id=course_id, usage_id=usage_id)
def submissions_score_reset_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_reset signal defined in the Submissions API, and convert it to a SCORE_CHANGED signal indicating that the score has been set to 0/0. Converts the unicode keys for user, course and item into the standard representation for the SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_reset): - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) if user is None: return SCORE_CHANGED.send(sender=None, points_earned=0, points_possible=0, user_id=user.id, course_id=course_id, usage_id=usage_id)
def _get_username(submission, user_id): """ Return username of student who provided `submission`. """ # If the student ID key doesn't exist, we're dealing with a single student and know the ID already. student_id = submission.get("student_id", user_id) return user_by_anonymous_id(student_id).username
def get_student_data(): # pylint: disable=no-member """ Returns a dict of student assignment information along with annotated file name, student id and module id, this information will be used on grading screen """ # Submissions doesn't have API for this, just use model directly. 直接操作model ,这一块是和成绩挂钩的关键 students = SubmissionsStudent.objects.filter( course_id=self.course_id, item_id=self.block_id) for student in students: submission = self.get_submission(student.student_id) if not submission: continue user = user_by_anonymous_id(student.student_id) module, created = StudentModule.objects.get_or_create( course_id=self.course_id, module_state_key=self.location, student=user, defaults={ 'state': '{}', 'module_type': self.category, }) if created: log.info( "Init for course:%s module:%s student:%s ", module.course_id, module.module_state_key, module.student.username ) state = json.loads(module.state) score = self.get_score(student.student_id) approved = score is not None if score is None: score = state.get('staff_score') needs_approval = score is not None else: needs_approval = False instructor = self.is_instructor() yield { 'module_id': module.id, 'student_id': student.student_id, 'submission_id': submission['uuid'], 'username': module.student.username, #'student_answer': self.student_answer, #?? maybe error 'student_answer': submission['answer']["student_answer"], 'fullname': module.student.profile.name, 'filename': submission['answer']["filename"], 'timestamp': submission['created_at'].strftime( DateTime.DATETIME_FORMAT ), 'score': score, 'approved': approved, 'needs_approval': instructor and needs_approval, 'may_grade': instructor or not approved, 'annotated': state.get("annotated_filename"), 'comment': state.get("comment", ''), }
def submissions_score_reset_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_reset signal defined in the Submissions API, and convert it to a SCORE_CHANGED signal indicating that the score has been set to 0/0. Converts the unicode keys for user, course and item into the standard representation for the SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_reset): - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ course_id = kwargs.get('course_id', None) usage_id = kwargs.get('item_id', None) user = None if 'anonymous_user_id' in kwargs: user = user_by_anonymous_id(kwargs.get('anonymous_user_id')) # If any of the kwargs were missing, at least one of the following values # will be None. if all((user, course_id, usage_id)): SCORE_CHANGED.send( sender=None, points_possible=0, points_earned=0, user=user, course_id=course_id, usage_id=usage_id ) else: log.exception( u"Failed to process score_reset signal from Submissions API. " "user: %s, course_id: %s, usage_id: %s", user, course_id, usage_id )
def submissions_score_reset_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_reset signal defined in the Submissions API, and convert it to a PROBLEM_WEIGHTED_SCORE_CHANGED signal indicating that the score has been set to 0/0. Converts the unicode keys for user, course and item into the standard representation for the PROBLEM_WEIGHTED_SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_reset): - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) if user is None: return PROBLEM_WEIGHTED_SCORE_CHANGED.send( sender=None, weighted_earned=0, weighted_possible=0, user_id=user.id, anonymous_user_id=kwargs['anonymous_user_id'], course_id=course_id, usage_id=usage_id, modified=kwargs['created_at'], score_deleted=True, score_db_table=ScoreDatabaseTableEnum.submissions, )
def store_student_response(self): """ Submit a student answer to the answer pool by appending the given answer to the end of the list. """ # if the answer is wrong, do not display it # if self.score != Credit.full.value: # return student_id = self.get_student_id() # remove any previous answers the student submitted for index, response in enumerate(self.displayable_answers): if response['student_id'] == student_id: del self.displayable_answers[index] break student_email = user_by_anonymous_id(student_id) self.displayable_answers.append({ 'student_id': student_id, 'student_email': student_email.email, 'answer': self.student_answer, })
def test_roundtrip_with_unicode_course_id(self): course2 = CourseFactory.create(display_name=u"Omega Course Ω") CourseEnrollment.enroll(self.user, course2.id) anonymous_id = anonymous_id_for_user(self.user, course2.id) real_user = user_by_anonymous_id(anonymous_id) self.assertEqual(self.user, real_user) self.assertEqual(anonymous_id, anonymous_id_for_user(self.user, course2.id, save=False))
def _get_student_submissions(block_id, course_id, locator): """ Returns valid submission file paths with the username of the student that submitted them. Args: course_id (unicode): edx course id block_id (unicode): edx block id locator (BlockUsageLocator): BlockUsageLocator for the sga module Returns: list(tuple): A list of 2-element tuples - (student username, submission file path) """ submissions = submissions_api.get_all_submissions( course_id, block_id, ITEM_TYPE ) return [ ( user_by_anonymous_id(submission['student_id']).username, get_file_storage_path( locator, submission['answer']['sha1'], submission['answer']['filename'] ) ) for submission in submissions if submission['answer'] ]
def submissions_score_set_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_set signal defined in the Submissions API, and convert it to a PROBLEM_WEIGHTED_SCORE_CHANGED signal defined in this module. Converts the unicode keys for user, course and item into the standard representation for the PROBLEM_WEIGHTED_SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_set): - 'points_possible': integer, - 'points_earned': integer, - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ points_possible = kwargs['points_possible'] points_earned = kwargs['points_earned'] course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) if user is None: return PROBLEM_WEIGHTED_SCORE_CHANGED.send(sender=None, weighted_earned=points_earned, weighted_possible=points_possible, user_id=user.id, course_id=course_id, usage_id=usage_id)
def submissions_score_reset_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_reset signal defined in the Submissions API, and convert it to a SCORE_CHANGED signal indicating that the score has been set to 0/0. Converts the unicode keys for user, course and item into the standard representation for the SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_reset): - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ course_id = kwargs.get('course_id', None) usage_id = kwargs.get('item_id', None) user = None if 'anonymous_user_id' in kwargs: user = user_by_anonymous_id(kwargs.get('anonymous_user_id')) # If any of the kwargs were missing, at least one of the following values # will be None. if all((user, course_id, usage_id)): SCORE_CHANGED.send(sender=None, points_possible=0, points_earned=0, user=user, course_id=course_id, usage_id=usage_id) else: log.exception( u"Failed to process score_reset signal from Submissions API. " "user: %s, course_id: %s, usage_id: %s", user, course_id, usage_id)
def submissions_score_reset_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_reset signal defined in the Submissions API, and convert it to a SCORE_CHANGED signal indicating that the score has been set to 0/0. Converts the unicode keys for user, course and item into the standard representation for the SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_reset): - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) # If any of the kwargs were missing, at least one of the following values # will be None. SCORE_CHANGED.send( sender=None, points_possible=0, points_earned=0, user=user, course_id=course_id, usage_id=usage_id )
def _get_username(submission, user_id): """ Return username of student who provided `submission`. """ # If the student ID key doesn't exist, we're dealing with a single student and know the ID already. student_id = submission.get('student_id', user_id) return user_by_anonymous_id(student_id).username
def test_roundtrip_for_logged_user(self): CourseEnrollment.enroll(self.user, self.course.id) anonymous_id = anonymous_id_for_user(self.user, self.course.id) real_user = user_by_anonymous_id(anonymous_id) self.assertEqual(self.user, real_user) self.assertEqual( anonymous_id, anonymous_id_for_user(self.user, self.course.id, save=False))
def get_student_data(): # pylint: disable=no-member """ Returns a dict of student assignment information along with annotated file name, student id and module id, this information will be used on grading screen """ # Submissions doesn't have API for this, just use model directly. students = SubmissionsStudent.objects.filter( course_id=self.course_id, item_id=self.block_id) for student in students: submission = self.get_submission(student.student_id) if not submission: continue user = user_by_anonymous_id(student.student_id) module, created = StudentModule.objects.get_or_create( course_id=self.course_id, module_state_key=self.location, student=user, defaults={ 'state': '{}', 'module_type': self.category, }) if created: log.info( "Init for course:%s module:%s student:%s ", module.course_id, module.module_state_key, module.student.username ) state = json.loads(module.state) score = self.get_score(student.student_id) approved = score is not None if score is None: score = state.get('staff_score') needs_approval = score is not None else: needs_approval = False instructor = self.is_instructor() yield { 'module_id': module.id, 'student_id': student.student_id, 'submission_id': submission['uuid'], 'username': module.student.username, 'fullname': module.student.profile.name, 'filename': submission['answer']["filename"], 'timestamp': submission['created_at'].strftime( DateTime.DATETIME_FORMAT ), 'score': score, 'approved': approved, 'needs_approval': instructor and needs_approval, 'may_grade': instructor or not approved, 'annotated': state.get("annotated_filename"), 'comment': state.get("comment", ''), }
def filter_assigments_by_team_members(self, assignments, members): """This method compares the team's users with an assigments' list""" for member in members: user = get_user_by_username_or_email(member["user"]["username"]) for assignment in assignments: if user == user_by_anonymous_id(assignment["student_id"]): assignment["profile_image_url"] = self._user_image_url( user) yield assignment
def get_student_data(): # pylint: disable=no-member """ Returns a dict of student assignment information along with annotated file name, student id and module id, this information will be used on grading screen """ # Submissions doesn't have API for this, just use model directly. students = SubmissionsStudent.objects.filter( course_id=self.course_id, item_id=self.block_id) for student in students: submission = self.get_submission(student.student_id) if not submission: continue user = user_by_anonymous_id(student.student_id) student_module = self.get_or_create_student_module(user) state = json.loads(student_module.state) score = self.get_score(student.student_id) approved = score is not None if score is None: score = state.get('staff_score') needs_approval = score is not None else: needs_approval = False instructor = self.is_instructor() yield { 'module_id': student_module.id, 'student_id': student.student_id, 'submission_id': submission['uuid'], 'username': student_module.student.username, 'fullname': student_module.student.profile.name, 'answer': submission['answer']["text"], 'timestamp': submission['created_at'].strftime( DateTime.DATETIME_FORMAT), 'score': score, 'approved': approved, 'needs_approval': instructor and needs_approval, 'may_grade': instructor or not approved, 'annotated': force_text(state.get("annotated_filename", '')), 'comment': force_text(state.get("comment", '')), 'finalized': is_finalized_submission(submission_data=submission) }
def get_student_data(): # Submissions doesn't have API for this, just use model directly students = SubmissionsStudent.objects.filter( course_id=self.course_id, item_id=self.block_id) for student in students: submission = self.get_submission(student.student_id) if not submission: continue user = user_by_anonymous_id(student.student_id) module, _ = StudentModule.objects.get_or_create( course_id=self.course_id, module_state_key=self.location, student=user, defaults={ 'state': '{}', 'module_type': self.category, }) state = json.loads(module.state) score = self.get_score(student.student_id) approved = score is not None if score is None: score = state.get('staff_score') needs_approval = score is not None else: needs_approval = False instructor = self.is_instructor() yield { 'module_id': module.id, 'student_id': student.student_id, 'submission_id': submission['uuid'], 'username': module.student.username, 'fullname': module.student.profile.name, 'filename': submission['answer']["filename"], 'timestamp': submission['created_at'].strftime( DateTime.DATETIME_FORMAT), 'score': score, 'approved': approved, 'needs_approval': instructor and needs_approval, 'may_grade': instructor or not approved, 'annotated': state.get("annotated_filename"), 'comment': state.get("comment", ''), }
def _get_graded_students(self): """ """ students = StudentItem.objects.filter(course_id=self.course_id, item_id=str(self.location)) result = [] for student in students: user = user_by_anonymous_id(student.student_id) if self.get_score(user): result.append(user) return result
def handle(self, *args, **options): if 'modified_start' not in options: raise CommandError('modified_start must be provided.') if 'modified_end' not in options: raise CommandError('modified_end must be provided.') modified_start = utc.localize( datetime.strptime(options['modified_start'], DATE_FORMAT)) modified_end = utc.localize( datetime.strptime(options['modified_end'], DATE_FORMAT)) event_transaction_id = create_new_event_transaction_id() set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE) kwargs = { 'modified__range': (modified_start, modified_end), 'module_type': 'problem' } for record in StudentModule.objects.filter(**kwargs): if not record.course_id.is_course: # This is not a course, so we don't store subsection grades for it. continue task_args = { "user_id": record.student_id, "course_id": six.text_type(record.course_id), "usage_id": six.text_type(record.module_state_key), "only_if_higher": False, "expected_modified_time": to_timestamp(record.modified), "score_deleted": False, "event_transaction_id": six.text_type(event_transaction_id), "event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE, "score_db_table": ScoreDatabaseTableEnum.courseware_student_module, } recalculate_subsection_grade_v3.apply_async(kwargs=task_args) kwargs = {'created_at__range': (modified_start, modified_end)} for record in Submission.objects.filter(**kwargs): if not record.student_item.course_id.is_course: # This is not a course, so ignore it continue task_args = { "user_id": user_by_anonymous_id(record.student_item.student_id).id, "anonymous_user_id": record.student_item.student_id, "course_id": six.text_type(record.student_item.course_id), "usage_id": six.text_type(record.student_item.item_id), "only_if_higher": False, "expected_modified_time": to_timestamp(record.created_at), "score_deleted": False, "event_transaction_id": six.text_type(event_transaction_id), "event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE, "score_db_table": ScoreDatabaseTableEnum.submissions, } recalculate_subsection_grade_v3.apply_async(kwargs=task_args)
def _get_user_info(submission, user_id): """ Return a (username, user id, user email) tuple for the student who provided `submission`. If the anonymous ID of the submission can't be resolved into a user, (student ID, 'N/A', 'N/A') is returned """ # If the student ID key doesn't exist, we're dealing with a single student and know the ID already. student_id = submission.get('student_id', user_id) user = user_by_anonymous_id(student_id) if user is None: return (student_id, 'N/A', 'N/A') return (user.username, user.id, user.email)
def _get_username(submission, user_id): """ Return username of student who provided `submission`. If the anonymous id of the submission can't be resolved into a username, the anonymous id is returned. """ # If the student ID key doesn't exist, we're dealing with a single student and know the ID already. student_id = submission.get('student_id', user_id) user = user_by_anonymous_id(student_id) if user is None: return student_id return user.username
def provide_context(self, context=None): """ Build a context dictionary to render the student view """ if self.get_student_id() != "student": user_is_admin = self.is_course_staff( user_by_anonymous_id(self.get_student_id()), self.course_id) else: user_is_admin = True context = context or {} context = dict(context) context.update({ 'display_name': self.display_name, 'indicator_class': self._get_indicator_class(), 'nodisplay_class': self._get_nodisplay_class(), 'problem_progress': self._get_problem_progress(), 'prompt': self.prompt, 'student_answer': self.student_answer, 'is_past_due': self.is_past_due(), 'used_attempts_feedback': self._get_used_attempts_feedback(), 'visibility_class': self._get_indicator_visibility_class(), 'word_count_message': self._get_word_count_message(), 'display_other_responses': self.display_other_student_responses, 'other_responses': self.get_other_answers(), 'user_is_admin': user_is_admin, 'user_alert': '', 'submitted_message': '', }) return context
def get_student_data(): # Submissions doesn't have API for this, just use model directly students = SubmissionsStudent.objects.filter( course_id=self.course_id, item_id=self.block_id) for student in students: submission = self.get_submission(student.student_id) if not submission: continue user = user_by_anonymous_id(student.student_id) module, _ = StudentModule.objects.get_or_create( course_id=self.course_id, module_state_key=self.location, student=user, defaults={ 'state': '{}', 'module_type': self.category, }) state = json.loads(module.state) score = self.get_score(student.student_id) approved = score is not None if score is None: score = state.get('staff_score') needs_approval = score is not None else: needs_approval = False instructor = self.is_instructor() yield { 'module_id': module.id, 'student_id': student.student_id, 'submission_id': submission['uuid'], 'username': module.student.username, 'fullname': module.student.profile.name, 'filename': submission['answer']["filename"], 'timestamp': submission['created_at'].strftime( DateTime.DATETIME_FORMAT ), 'score': score, 'approved': approved, 'needs_approval': instructor and needs_approval, 'may_grade': instructor or not approved, 'annotated': state.get("annotated_filename"), 'comment': state.get("comment", ''), }
def get_student_data(): # pylint: disable=no-member """ Returns a dict of student assignment information along with annotated file name, student id and module id, this information will be used on grading screen """ # Submissions doesn't have API for this, just use model directly. students = SubmissionsStudent.objects.filter( course_id=self.course_id, item_id=self.block_id) for student in students: submission = self.get_submission(student.student_id) if not submission: continue user = user_by_anonymous_id(student.student_id) student_module = self.get_or_create_student_module(user) state = json.loads(student_module.state) score = self.get_score(student.student_id) approved = score is not None if score is None: score = state.get('staff_score') needs_approval = score is not None else: needs_approval = False instructor = self.is_instructor() yield { 'module_id': student_module.id, 'student_id': student.student_id, 'submission_id': submission['uuid'], 'username': student_module.student.username, 'fullname': student_module.student.profile.name, 'filename': submission['answer']["filename"], 'timestamp': submission['created_at'].strftime( DateTime.DATETIME_FORMAT ), 'score': score, 'approved': approved, 'needs_approval': instructor and needs_approval, 'may_grade': instructor or not approved, 'annotated': force_text(state.get("annotated_filename", '')), 'comment': force_text(state.get("comment", '')), 'finalized': is_finalized_submission(submission_data=submission) }
def get_encoded_data(self): """ Collect all data needed and encode it Returns: string """ if self.is_course_staff(): return "" user = user_by_anonymous_id(self.runtime.anonymous_student_id) data = { 'course_id': str(self.course_id), 'username': user.username, 'email': user.email, 'fullname': "%s %s" % (user.first_name, user.last_name) } row = json.dumps(data) encoded = AESCipher(settings.FEATURES['REDDIT_SECRET_KEY']).encrypt(row) return "?data=" + urllib.quote(encoded)
def handle(self, *args, **options): if 'modified_start' not in options: raise CommandError('modified_start must be provided.') if 'modified_end' not in options: raise CommandError('modified_end must be provided.') modified_start = utc.localize(datetime.strptime(options['modified_start'], DATE_FORMAT)) modified_end = utc.localize(datetime.strptime(options['modified_end'], DATE_FORMAT)) event_transaction_id = create_new_event_transaction_id() set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE) kwargs = {'modified__range': (modified_start, modified_end), 'module_type': 'problem'} for record in StudentModule.objects.filter(**kwargs): task_args = { "user_id": record.student_id, "course_id": unicode(record.course_id), "usage_id": unicode(record.module_state_key), "only_if_higher": False, "expected_modified_time": to_timestamp(record.modified), "score_deleted": False, "event_transaction_id": unicode(event_transaction_id), "event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE, "score_db_table": ScoreDatabaseTableEnum.courseware_student_module, } recalculate_subsection_grade_v3.apply_async(kwargs=task_args) kwargs = {'created_at__range': (modified_start, modified_end)} for record in Submission.objects.filter(**kwargs): task_args = { "user_id": user_by_anonymous_id(record.student_item.student_id).id, "anonymous_user_id": record.student_item.student_id, "course_id": unicode(record.student_item.course_id), "usage_id": unicode(record.student_item.item_id), "only_if_higher": False, "expected_modified_time": to_timestamp(record.created_at), "score_deleted": False, "event_transaction_id": unicode(event_transaction_id), "event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE, "score_db_table": ScoreDatabaseTableEnum.submissions, } recalculate_subsection_grade_v3.apply_async(kwargs=task_args)
def post_comment(self, data, suffix=''): #IT MIGHT BE BETTER TO MAINTAIN THE PRIMARY KEYS than rely on SQL auto increment since it is hard to get back the primary key. ajax_comment = data.get('comment') """ The ajax supplied comment may have ' (apostrophe/single quote) embedded in it. These must be escaped before being put into the SQL query (which itself relies on single quotes when inserting strings). """ safe_comment = "" self.xblock_login = self.student_id() current_user = user_by_anonymous_id(self.xblock_login) for char in ajax_comment: if (char != "'"): safe_comment += char else: safe_comment +="\\'" #Escaping the embedded single quote using a single \. We use \\ to escape it in python as well #ALSO CHECK DOUBLE QUOTES AND WILDCARD CHARACTER CASES!!! insert_query = ("INSERT INTO discussion_table (thread_id, user_id, user_name, comment, parent_id) VALUES (2, '" + self.xblock_login + "', '" + current_user.username + "', '" + safe_comment + "', -1)") ret_val = self.exec_query(insert_query,"Inserting user comment") if(ret_val == 0): return {'update_status': "Success"} else: return {'update_status': "Failure"}
def student_state(self): """ Returns a JSON serializable representation of student's state for rendering in client view. """ if self.annotated_sha1: annotated = {"filename": self.annotated_filename} else: annotated = None if self.xmodule_runtime.anonymous_student_id: user = user_by_anonymous_id( self.xmodule_runtime.anonymous_student_id) score = 0 if user: student_record, created = StudentModule.objects.get_or_create( course_id=self.course_id, module_state_key=self.location, student=user, defaults={ 'state': '{}', 'module_type': self.category, 'grade': 0, 'max_grade': self.max_score() }) score = student_record.grade if score is not None: graded = {'score': score, 'comment': self.comment} else: graded = None return { "display_name": self.display_name, "annotated": annotated, "graded": graded, "max_score": self.max_score(), "upload_allowed": self.upload_allowed(), }
def submissions_score_set_handler(sender, **kwargs): # pylint: disable=unused-argument """ Consume the score_set signal defined in the Submissions API, and convert it to a PROBLEM_WEIGHTED_SCORE_CHANGED signal defined in this module. Converts the unicode keys for user, course and item into the standard representation for the PROBLEM_WEIGHTED_SCORE_CHANGED signal. This method expects that the kwargs dictionary will contain the following entries (See the definition of score_set): - 'points_possible': integer, - 'points_earned': integer, - 'anonymous_user_id': unicode, - 'course_id': unicode, - 'item_id': unicode """ points_possible = kwargs['points_possible'] points_earned = kwargs['points_earned'] course_id = kwargs['course_id'] usage_id = kwargs['item_id'] user = user_by_anonymous_id(kwargs['anonymous_user_id']) if user is None: return if points_possible == 0: # This scenario is known to not succeed, see TNL-6559 for details. return PROBLEM_WEIGHTED_SCORE_CHANGED.send( sender=None, weighted_earned=points_earned, weighted_possible=points_possible, user_id=user.id, anonymous_user_id=kwargs['anonymous_user_id'], course_id=course_id, usage_id=usage_id, modified=kwargs['created_at'], score_db_table=ScoreDatabaseTableEnum.submissions, )
def test_roundtrip_for_logged_user(self): CourseEnrollment.enroll(self.user, self.course.id) anonymous_id = anonymous_id_for_user(self.user, self.course.id) real_user = user_by_anonymous_id(anonymous_id) self.assertEqual(self.user, real_user) self.assertEqual(anonymous_id, anonymous_id_for_user(self.user, self.course.id, save=False))
def test_for_unregistered_user(self): # same path as for logged out user self.assertEqual(None, anonymous_id_for_user(AnonymousUser(), self.course.id)) self.assertIsNone(user_by_anonymous_id(None))
def student_view(self, context): """ Create a fragment used to display the XBlock to a student. `context` is a dictionary used to configure the display (unused). Returns a `Fragment` object specifying the HTML, CSS, and JavaScript to display. """ attempts = None max_attempts = None score = None passing = False is_attempted = False # On first adding the block, the studio calls student_view instead of # author_view, which causes problems. Force call of the author view. if getattr(self.runtime, 'is_author_mode', False): return self.author_view() if EDX_FOUND: total_correct, total_possible = 0, 0 target_block_id = self.graded_target_id course_id = target_block_id.course_key.for_branch( None).version_agnostic() target_block = self.runtime.get_block(target_block_id) student = user_by_anonymous_id(self.runtime.anonymous_student_id) count = 0 if student: def create_module(descriptor): return target_block.runtime.get_block(descriptor.location) for module_descriptor in yield_dynamic_descriptor_descendents( target_block, create_module): (correct, total) = get_score(course_id, student, module_descriptor, create_module) if (correct is None and total is None) or (not total > 0): continue # Note we ignore the 'graded' flag since authors may wish to use a leaderboard for non-graded content total_correct += correct total_possible += total count += 1 attempts = module_descriptor.problem_attempts max_attempts = module_descriptor.max_attempts is_attempted = module_descriptor.is_attempted embed_code = module_descriptor.get_problem_html else: embed_code = "aaaa" else: embed_code = "Error: EdX not found." #lets do some math to see if we passed score = 0 # calculate score passing = False if total_possible > 0: score = total_correct / total_possible * 100 #get score out of 100, not 1 if score >= 80: passing = True if attempts > 0 or is_attempted == True: # student has submitted this assessment result_file = "assessmentendcap.html" if passing: result_file = "assessmentendcap-pass.html" else: if max_attempts and attempts < max_attempts: # student can still submit this problem again result_file = "assessmentendcap-tryagain.html" elif attempts >= max_attempts: # student has submitted this problem the max number of times result_file = "assessmentendcap-fail.html" else: #student has not submitted assessment. We don't need to render anything. result_file = "assessmentendcap-blank.html" return self.create_fragment( "static/html/" + result_file, context={ 'embed_code': embed_code, 'total_correct': total_correct, 'total_possible': total_possible, 'score': score, 'attempts': attempts, 'max_attempts': max_attempts, 'count': count, 'is_attempted': is_attempted }, javascript=["static/js/assessmentendcap.js"], initialize='AssessmentEndcapXBlock')
def main(): from django.contrib.auth.models import User from openassessment.assessment.models import Assessment, AssessmentPart, StaffWorkflow from openassessment.workflow.models import AssessmentWorkflow, AssessmentWorkflowStep from student.models import anonymous_id_for_user, user_by_anonymous_id from submissions.models import Score, ScoreSummary, ScoreAnnotation, Submission old_scores = Score.objects.filter(submission__isnull=True, reset=False).order_by('id') updated_count = 0 for score in old_scores: try: with transaction.atomic(): # ScoreSummary is updated on Score saves but for this script we don't want that. # Correct way is to disconnect post_save signal, but since the receiver function # is defined in the class, we can't reference it. Workaround here is to just # prefetch the score summary and resave it to maintain its original field values. score_summary = ScoreSummary.objects.get(student_item=score.student_item) # Update old override with submission from the score preceding it. # If none exists, look for it in the submissions table. preceding_score = Score.objects.filter( student_item=score.student_item, created_at__lte=score.created_at, submission__isnull=False, ).order_by('-created_at')[:1] if preceding_score.count(): submission = preceding_score.get().submission else: submission_qset = Submission.objects.filter(student_item=score.student_item) if submission_qset.count() > 1: raise Exception("MULTIPLE SUBMISSIONS FOR STUDENT_ITEM {}".format(score.student_item)) else: submission = submission_qset[:1].get() score.submission = submission score.save() # Offset override reset by 1 second for convenience when sorting db override_date = score.created_at - datetime.timedelta(seconds=1) # Create reset score Score.objects.create( student_item=score.student_item, submission=None, points_earned=0, points_possible=0, created_at=override_date, reset=True, ) # Restore original score summary values score_summary.save() # Fetch staff id from score course for ScoreAnnotation course_id = CourseKey.from_string(score.student_item.course_id) staff = User.objects.filter( courseaccessrole__role__in=['instructor', 'staff'], courseaccessrole__course_id=course_id, )[:1].get() staff_id = anonymous_id_for_user(staff, course_id, save=False) # Create ScoreAnnotation score_annotation = ScoreAnnotation( score=score, annotation_type="staff_defined", creator=staff_id, reason="A staff member has defined the score for this submission", ) score_annotation.save() # ORA2 Table Updates... # Fetch rubric from an existing assessment assessment = Assessment.objects.filter(submission_uuid=submission.uuid)[:1].get() rubric = assessment.rubric staff_assessment = Assessment.create( rubric=rubric, scorer_id=staff_id, submission_uuid=submission.uuid, score_type="ST", scored_at=override_date, ) # Fake criterion selections rubric_index = rubric.index assessment_parts = [] criteria_without_options = rubric_index.find_criteria_without_options() criteria_with_options = set(rubric_index._criteria_index.values()) - criteria_without_options ordered_criteria = sorted(criteria_with_options, key=lambda criterion: criterion.order_num) criteria_options = [c.options.all() for c in ordered_criteria] # Just take the first combination of options which add up to the override point score for selection in itertools.product(*criteria_options): total = sum(option.points for option in selection) if total == score.points_earned: for option in selection: assessment_parts.append({ 'criterion': option.criterion, 'option': option }) break # Default to first option for each criteria if no matching sum found if not assessment_parts: print "NO CLEAN SUM FOR SUBMISSION " + submission.uuid for options in criteria_options: assessment_parts.append({ 'criterion': options[0].criterion, 'option': options[0], }) # Add feedback-only criteria for criterion in criteria_without_options: assessment_parts.append({ 'criterion': criterion, 'option': None }) AssessmentPart.objects.bulk_create([ AssessmentPart( assessment=staff_assessment, criterion=assessment_part['criterion'], option=assessment_part['option'], feedback=u"" ) for assessment_part in assessment_parts ]) try: staff_workflow = StaffWorkflow.objects.get(submission_uuid=submission.uuid) staff_workflow.assessment = staff_assessment.id staff_workflow.grading_completed_at = override_date except StaffWorkflow.DoesNotExist: staff_workflow = StaffWorkflow( scorer_id=staff_id, course_id=score.student_item.course_id, item_id=score.student_item.item_id, submission_uuid=submission.uuid, created_at=override_date, grading_completed_at=override_date, assessment=staff_assessment.id, ) staff_workflow.save() workflow = AssessmentWorkflow.get_by_submission_uuid(submission.uuid) try: staff_step = workflow.steps.get(name='staff') staff_step.assessment_completed_at = score.created_at staff_step.submitter_completed_at = score.created_at staff_step.save() except AssessmentWorkflowStep.DoesNotExist: for step in workflow.steps.all(): step.assessment_completed_at = score.created_at step.submitter_completed_at = score.created_at step.order_num += 1 step.save() workflow.steps.add( AssessmentWorkflowStep( name='staff', order_num=0, assessment_completed_at=score.created_at, submitter_completed_at=score.created_at, ) ) # Update workflow status to done if it wasn't subsequently cancelled if workflow.status != 'cancelled': workflow.status = 'done' workflow.save() updated_count += 1 user = user_by_anonymous_id(score.student_item.student_id) print( "Successfully updated score {} for user {} with email {} in course {} for item: {}".format( score.id, user.username, user.email, score.student_item.course_id, score.student_item.item_id, ) ) except Exception as err: print("An error occurred updating score {}: {}".format(score.id, err)) print("Please update this score manually and retry script.") break print("Script finished, number of scores updated: {}.".format(updated_count))
def handle(self, *args, **options): if len(args) != 1: raise CommandError( "This command requires only one argument: <course_id>") course_id, = args # Check args: course_id try: course_id = CourseLocator.from_string(course_id) except InvalidKeyError: raise CommandError( "The course_id is not of the right format. It should be like 'org/course/run' or 'course-v1:org+course+run'" ) if not modulestore().get_course(course_id): raise CommandError("No such course was found.") # S3 store try: conn = connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY) bucket = conn.get_bucket(settings.FILE_UPLOAD_STORAGE_BUCKET_NAME) except Exception as e: print e raise CommandError( "Could not establish a connection to S3 for file upload. Check your credentials." ) # Dump directory dump_dir = options['dump_dir'] or '/tmp/dump_oa_scores' if not os.path.exists(dump_dir): os.makedirs(dump_dir) # Whether to gather attachments with_attachments = options['with_attachments'] # Find openassessment items oa_items = modulestore().get_items( course_id, qualifiers={'category': 'openassessment'}) if not oa_items: raise CommandError("No openassessment item was found.") oa_items = sorted( oa_items, key=lambda item: item.start or datetime(2030, 1, 1, tzinfo=UTC())) print "Openassessment item(s):" oa_output = PrettyTable(['#', 'Item ID', 'Title']) oa_output.align = 'l' for i, oa_item in enumerate(oa_items): row = [] row.append(i) row.append(oa_item.location) row.append(oa_item.title) oa_output.add_row(row) print oa_output while True: try: selected = raw_input( "Choose an openassessment item # (empty to cancel): ") if selected == '': print "Cancelled." return selected_oa_item = int(selected) oa_item = oa_items[selected_oa_item] break except (IndexError, ValueError): print "WARN: Invalid number was detected. Choose again." continue item_location = oa_item.location # Get submissions from course_id and item_location submissions = get_submissions(course_id, item_location) header = [ 'Title', 'User name', 'Submission content', 'Submission created at', 'Status', 'Points earned', 'Points possible', 'Score created at', 'Grade count', 'Being graded count', 'Scored count' ] header_extra = [] rows = [] for submission in submissions: row = [] # 'Title' row.append(oa_item.title) print 'submission_uuid=%s' % submission.submission_uuid # 'User Name' user = user_by_anonymous_id(submission.student_id) row.append(user.username) # 'Submission Content' raw_answer = Submission.objects.get( uuid=submission.submission_uuid).raw_answer row.append(json.loads(raw_answer)['text'].replace('\n', '[\\n]')) # 'Submission created at' row.append(submission.created_at) # 'Status' row.append( get_workflow_info(submission.submission_uuid, oa_item)['status']) # 'Points earned', 'Points possible', 'Score created at' latest_score = get_latest_score(submission) if latest_score is not None: row.append(latest_score.points_earned) row.append(latest_score.points_possible) row.append(latest_score.created_at) else: row.append('') row.append('') row.append('') # 'Grade count' row.append(submission.num_peers_graded()) # 'Being graded count' row.append( len( PeerWorkflowItem.objects.filter( author=submission.id, submission_uuid=submission.submission_uuid, assessment__isnull=False))) # 'Scored count' row.append( len( PeerWorkflowItem.objects.filter( author=submission.id, submission_uuid=submission.submission_uuid, assessment__isnull=False, scored=True))) # assessments from others assessed_items = PeerWorkflowItem.objects.filter( author=submission.id, submission_uuid=submission.submission_uuid, assessment__isnull=False).order_by('assessment') if assessed_items: # 'Scored flags' header_extra.append('Scored flags') row.append(','.join([ str(int(assessed_item.scored)) for assessed_item in assessed_items ])) # 'Scorer Usernames' header_extra.append('Scorer usernames') scorer_usernames = [ user_by_anonymous_id( assessed_item.scorer.student_id).username for assessed_item in assessed_items ] row.append(','.join(scorer_usernames)) assessed_assessments = [ assessed_item.assessment for assessed_item in assessed_items ] scores = scores_by_criterion(assessed_assessments) # 'Rubric points' for i, score in enumerate(scores.items()): header_extra.append('Rubric(%s) points' % score[0]) row.append(','.join(map(str, score[1]))) else: pass rows.append(row) # Create csv file header.extend(sorted(set(header_extra), key=header_extra.index)) csv_filename = 'oa_scores-%s-#%d.csv' % ( course_id.to_deprecated_string().replace('/', '.'), selected_oa_item) csv_filepath = os.path.join(dump_dir, csv_filename) write_csv(csv_filepath, header, rows) # Upload to S3 upload_file_to_s3(bucket, csv_filename, csv_filepath) # Download images from S3 if with_attachments: temp_dir = tempfile.mkdtemp() for submission in submissions: file_key = u"{prefix}/{student_id}/{course_id}/{item_id}".format( prefix=settings.FILE_UPLOAD_STORAGE_PREFIX, student_id=submission.student_id, course_id=course_id.to_deprecated_string(), item_id=oa_item.location.to_deprecated_string(), ) try: key = bucket.get_key(file_key) except: print "WARN: No such file in S3 [%s]" % file_key continue user = user_by_anonymous_id(submission.student_id) user_path = os.path.join(temp_dir, user.username) try: key.get_contents_to_filename(user_path) except: print "WARN: Could not download file from S3 [%s]" % file_key continue # Compress and upload to S3 tar_filename = 'oa_scores-%s-#%d.tar.gz' % ( course_id.to_deprecated_string().replace( '/', '.'), selected_oa_item) tar_filepath = os.path.join(dump_dir, tar_filename) tar = tarfile.open(tar_filepath, 'w:gz') tar.add(temp_dir, arcname=tar_filename) shutil.rmtree(temp_dir) upload_file_to_s3(bucket, tar_filename, tar_filepath)
def student_view(self, context): """ Create a fragment used to display the XBlock to a student. `context` is a dictionary used to configure the display (unused). Returns a `Fragment` object specifying the HTML, CSS, and JavaScript to display. """ attempts = None max_attempts = None score = None passing = False is_attempted = False # On first adding the block, the studio calls student_view instead of # author_view, which causes problems. Force call of the author view. if getattr(self.runtime, 'is_author_mode', False): return self.author_view() if EDX_FOUND: total_correct, total_possible = 0, 0 target_block_id = self.graded_target_id course_id = target_block_id.course_key.for_branch(None).version_agnostic() target_block = self.runtime.get_block(target_block_id) student = user_by_anonymous_id(self.runtime.anonymous_student_id) count = 0 if student: def create_module(descriptor): return target_block.runtime.get_block(descriptor.location) for module_descriptor in yield_dynamic_descriptor_descendents(target_block, create_module): (correct, total) = get_score(course_id, student, module_descriptor, create_module) if (correct is None and total is None) or (not total > 0): continue # Note we ignore the 'graded' flag since authors may wish to use a leaderboard for non-graded content total_correct += correct total_possible += total count += 1 attempts = module_descriptor.problem_attempts max_attempts = module_descriptor.max_attempts is_attempted = module_descriptor.is_attempted embed_code = module_descriptor.get_problem_html else: embed_code = "aaaa" else: embed_code = "Error: EdX not found." #lets do some math to see if we passed score = 0 # calculate score passing = False if total_possible > 0: score = total_correct / total_possible * 100 #get score out of 100, not 1 if score >= 80: passing = True if attempts > 0 or is_attempted == True: # student has submitted this assessment result_file="assessmentendcap.html" if passing: result_file = "assessmentendcap-pass.html" else: if max_attempts and attempts < max_attempts: # student can still submit this problem again result_file = "assessmentendcap-tryagain.html" elif attempts >= max_attempts: # student has submitted this problem the max number of times result_file = "assessmentendcap-fail.html" else: #student has not submitted assessment. We don't need to render anything. result_file="assessmentendcap-blank.html" return self.create_fragment( "static/html/" + result_file, context={ 'embed_code': embed_code, 'total_correct': total_correct, 'total_possible': total_possible, 'score': score, 'attempts': attempts, 'max_attempts': max_attempts, 'count' : count, 'is_attempted': is_attempted }, javascript=["static/js/assessmentendcap.js"], initialize='AssessmentEndcapXBlock' )
def handle(self, *args, **options): if len(args) != 1: raise CommandError("This command requires only one argument: <course_id>") course_id, = args # Check args: course_id try: course_id = CourseLocator.from_string(course_id) except InvalidKeyError: raise CommandError("The course_id is not of the right format. It should be like 'org/course/run' or 'course-v1:org+course+run'") if not modulestore().get_course(course_id): raise CommandError("No such course was found.") # S3 store try: conn = connect_s3(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY) bucket = conn.get_bucket(settings.FILE_UPLOAD_STORAGE_BUCKET_NAME) except Exception as e: print e raise CommandError("Could not establish a connection to S3 for file upload. Check your credentials.") # Dump directory dump_dir = options['dump_dir'] or '/tmp/dump_oa_scores' if not os.path.exists(dump_dir): os.makedirs(dump_dir) # Whether to gather attachments with_attachments = options['with_attachments'] # Find openassessment items oa_items = modulestore().get_items(course_id, qualifiers={'category': 'openassessment'}) if not oa_items: raise CommandError("No openassessment item was found.") oa_items = sorted(oa_items, key=lambda item:item.start or datetime(2030, 1, 1, tzinfo=UTC())) print "Openassessment item(s):" oa_output = PrettyTable(['#', 'Item ID', 'Title']) oa_output.align = 'l' for i, oa_item in enumerate(oa_items): row = [] row.append(i) row.append(oa_item.location) row.append(oa_item.title) oa_output.add_row(row) print oa_output while True: try: selected = raw_input("Choose an openassessment item # (empty to cancel): ") if selected == '': print "Cancelled." return selected_oa_item = int(selected) oa_item = oa_items[selected_oa_item] break except (IndexError, ValueError): print "WARN: Invalid number was detected. Choose again." continue item_location = oa_item.location # Get submissions from course_id and item_location submissions = get_submissions(course_id, item_location) header = ['Title', 'User name', 'Submission content', 'Submission created at', 'Status', 'Points earned', 'Points possible', 'Score created at', 'Grade count', 'Being graded count', 'Scored count'] header_extra = [] rows = [] for submission in submissions: row = [] # 'Title' row.append(oa_item.title) print 'submission_uuid=%s' % submission.submission_uuid # 'User Name' user = user_by_anonymous_id(submission.student_id) row.append(user.username) # 'Submission Content' raw_answer = Submission.objects.get(uuid=submission.submission_uuid).raw_answer row.append(json.loads(raw_answer)['text'].replace('\n', '[\\n]')) # 'Submission created at' row.append(submission.created_at) # 'Status' row.append(get_workflow_info(submission.submission_uuid, oa_item)['status']) # 'Points earned', 'Points possible', 'Score created at' latest_score = get_latest_score(submission) if latest_score is not None: row.append(latest_score.points_earned) row.append(latest_score.points_possible) row.append(latest_score.created_at) else: row.append('') row.append('') row.append('') # 'Grade count' row.append(submission.num_peers_graded()) # 'Being graded count' row.append(len(PeerWorkflowItem.objects.filter(author=submission.id, submission_uuid=submission.submission_uuid, assessment__isnull=False))) # 'Scored count' row.append(len(PeerWorkflowItem.objects.filter(author=submission.id, submission_uuid=submission.submission_uuid, assessment__isnull=False, scored=True))) # assessments from others assessed_items = PeerWorkflowItem.objects.filter(author=submission.id, submission_uuid=submission.submission_uuid, assessment__isnull=False).order_by('assessment') if assessed_items: # 'Scored flags' header_extra.append('Scored flags') row.append(','.join([str(int(assessed_item.scored)) for assessed_item in assessed_items])) # 'Scorer Usernames' header_extra.append('Scorer usernames') scorer_usernames = [user_by_anonymous_id(assessed_item.scorer.student_id).username for assessed_item in assessed_items] row.append(','.join(scorer_usernames)) assessed_assessments = [assessed_item.assessment for assessed_item in assessed_items] scores = scores_by_criterion(assessed_assessments) # 'Rubric points' for i, score in enumerate(scores.items()): header_extra.append('Rubric(%s) points' % score[0]) row.append(','.join(map(str, score[1]))) else: pass rows.append(row) # Create csv file header.extend(sorted(set(header_extra), key=header_extra.index)) csv_filename = 'oa_scores-%s-#%d.csv' % (course_id.to_deprecated_string().replace('/', '.'), selected_oa_item) csv_filepath = os.path.join(dump_dir, csv_filename) write_csv(csv_filepath, header, rows) # Upload to S3 upload_file_to_s3(bucket, csv_filename, csv_filepath) # Download images from S3 if with_attachments: temp_dir = tempfile.mkdtemp() for submission in submissions: file_key = u"{prefix}/{student_id}/{course_id}/{item_id}".format( prefix=settings.FILE_UPLOAD_STORAGE_PREFIX, student_id=submission.student_id, course_id=course_id.to_deprecated_string(), item_id=oa_item.location.to_deprecated_string(), ) try: key = bucket.get_key(file_key) except: print "WARN: No such file in S3 [%s]" % file_key continue user = user_by_anonymous_id(submission.student_id) user_path = os.path.join(temp_dir, user.username) try: key.get_contents_to_filename(user_path) except: print "WARN: Could not download file from S3 [%s]" % file_key continue # Compress and upload to S3 tar_filename = 'oa_scores-%s-#%d.tar.gz' % (course_id.to_deprecated_string().replace('/', '.'), selected_oa_item) tar_filepath = os.path.join(dump_dir, tar_filename) tar = tarfile.open(tar_filepath, 'w:gz') tar.add(temp_dir, arcname=tar_filename) shutil.rmtree(temp_dir) upload_file_to_s3(bucket, tar_filename, tar_filepath)
def get_user_by_anonymous_id(*args, **kwargs): """ Returns the user_by_anonymous_id method. """ return user_by_anonymous_id(*args, **kwargs)