Ejemplo n.º 1
0
 def test_anonymous_id_secret_key_changes_do_not_change_existing_anonymous_ids(self):
     """Test that a same 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)
         assert anonymous_id == new_anonymous_id
         assert self.user == user_by_anonymous_id(anonymous_id)
         assert self.user == user_by_anonymous_id(new_anonymous_id)
Ejemplo n.º 2
0
 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))
Ejemplo n.º 3
0
 def is_staff(self):
     cohort = get_cohort(user_by_anonymous_id(
         self.xmodule_runtime.anonymous_student_id),
                         self.course_id,
                         assign=False,
                         use_cached=True)
     return super().is_staff or (cohort and cohort.name == PROFS_COHORT)
Ejemplo n.º 4
0
 def test_roundtrip_with_unicode_course_id(self):
     course2 = CourseFactory.create(display_name="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)
     assert self.user == real_user
     assert anonymous_id == anonymous_id_for_user(self.user, course2.id)
Ejemplo n.º 5
0
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,
    )
Ejemplo n.º 6
0
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']
    ]
Ejemplo n.º 7
0
 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))
Ejemplo n.º 8
0
 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))
Ejemplo n.º 9
0
    def get_user_by_anonymous_id(self, uid=None):
        """
        Returns the Django User object corresponding to the given anonymous user id.

        Returns None if there is no user with the given anonymous user id.

        If no `uid` is provided, then the current anonymous user ID is used.
        """
        return user_by_anonymous_id(uid or self._anonymous_user_id)
Ejemplo n.º 10
0
 def get_student_data():
     """
     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)
         }
Ejemplo n.º 11
0
 def test_anonymous_id_secret_key_changes_result_in_diff_values_for_same_new_user(self):
     """Test that a different 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)
         AnonymousUserId.objects.filter(user=self.user).filter(course_id=self.course.id).delete()
         new_anonymous_id = anonymous_id_for_user(self.user, self.course.id)
         assert anonymous_id != new_anonymous_id
         assert self.user == user_by_anonymous_id(new_anonymous_id)
    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": str(record.course_id),
                "usage_id": str(record.module_state_key),
                "only_if_higher": False,
                "expected_modified_time": to_timestamp(record.modified),
                "score_deleted": False,
                "event_transaction_id": str(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": str(record.student_item.course_id),
                "usage_id": str(record.student_item.item_id),
                "only_if_higher": False,
                "expected_modified_time": to_timestamp(record.created_at),
                "score_deleted": False,
                "event_transaction_id": str(event_transaction_id),
                "event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE,
                "score_db_table": ScoreDatabaseTableEnum.submissions,
            }
            recalculate_subsection_grade_v3.apply_async(kwargs=task_args)
Ejemplo n.º 13
0
    def get_sorted_submissions(self):
        """returns student recent assignments sorted on date"""
        assignments = []
        submissions = submissions_api.get_all_submissions(
            self.block_course_id, self.block_id, ITEM_TYPE)

        for submission in submissions:
            student = user_by_anonymous_id(submission['student_id'])
            sub = {
                'submission_id':
                submission['uuid'],
                'username':
                student.username,
                'student_id':
                submission['student_id'],
                'fullname':
                student.profile.name,
                'timestamp':
                submission['submitted_at'] or submission['created_at'],
                'filename':
                submission['answer']["filename"],
                'score':
                json.loads(submission['answer']['score'])
                if 'score' in submission['answer'] else 0,
                'result':
                json.loads(submission['answer']['result'])
            }
            if is_course_cohorted(self.course_id):
                group = get_cohort(student,
                                   self.course_id,
                                   assign=False,
                                   use_cached=True)
                sub['cohort'] = group.name if group else '(não atribuído)'
            assignments.append(sub)

        assignments.sort(key=lambda assignment: assignment['timestamp'],
                         reverse=True)
        return assignments
Ejemplo n.º 14
0
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,
    )
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
 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))
Ejemplo n.º 17
0
 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)
     assert self.user == real_user
     assert anonymous_id == anonymous_id_for_user(self.user, self.course.id)
Ejemplo n.º 18
0
 def test_for_unregistered_user(self):  # same path as for logged out user
     assert anonymous_id_for_user(AnonymousUser(), self.course.id) is None
     assert user_by_anonymous_id(None) is None
Ejemplo n.º 19
0
 def user_id(self):
     return user_by_anonymous_id(
         self.xmodule_runtime.anonymous_student_id).id