Example #1
0
    def test_nonhuman_committers_not_counted(self):
        """Test that only human committers are counted as contributors.
        """
        # Create a commit with the system user id.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, feconf.SYSTEM_COMMITTER_ID, title='Original Title')
        # Run the job to compute the contributor ids.
        job_id = exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new()
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        # Check that the system id was not added to the exploration's
        # contributor ids.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertNotIn(
            feconf.SYSTEM_COMMITTER_ID,
            exploration_summary.contributor_ids)

        # Create a commit with the migration bot user id.
        exploration_model = exp_models.ExplorationModel.get(
            self.EXP_ID, strict=True, version=None)
        exploration_model.title = 'New title'
        exploration_model.commit(
            feconf.MIGRATION_BOT_USERNAME, 'Changed title.', [])
        # Run the job to compute the contributor ids.
        job_id = exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new()
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        # Check that the migration bot id was not added to the exploration's
        # contributor ids.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertNotIn(
            feconf.MIGRATION_BOT_USERNAME, exploration_summary.contributor_ids)
Example #2
0
    def test_nonhuman_committers_not_counted(self):
        """Test that only human committers are counted as contributors.
        """
        # Create a commit with the system user id.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, feconf.SYSTEM_COMMITTER_ID, title='Original Title')
        # Run the job to compute the contributor ids.
        job_id = exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new()
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        # Check that the system id was not added to the exploration's
        # contributor ids.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertNotIn(
            feconf.SYSTEM_COMMITTER_ID,
            exploration_summary.contributor_ids)

        # Create a commit with the migration bot user id.
        exploration_model = exp_models.ExplorationModel.get(
            self.EXP_ID, strict=True, version=None)
        exploration_model.title = 'New title'
        exploration_model.commit(
            feconf.MIGRATION_BOT_USERNAME, 'Changed title.', [])
        # Run the job to compute the contributor ids.
        job_id = exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new()
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        # Check that the migration bot id was not added to the exploration's
        # contributor ids.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertNotIn(
            feconf.MIGRATION_BOT_USERNAME, exploration_summary.contributor_ids)
Example #3
0
    def test_search_ranks_cannot_be_negative(self):
        self.save_new_valid_exploration(self.EXP_ID, self.owner_id)
        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)

        base_search_rank = 20

        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary),
            base_search_rank)

        # A user can (down-)rate an exploration at most once.
        for i in xrange(50):
            rating_services.assign_rating_to_exploration(
                'user_id_1', self.EXP_ID, 1)
        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary),
            base_search_rank - 5)

        for i in xrange(50):
            rating_services.assign_rating_to_exploration(
                'user_id_%s' % i, self.EXP_ID, 1)

        # The rank will be at least 0.
        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary), 0)
Example #4
0
    def test_get_search_rank(self):
        self.save_new_valid_exploration(self.EXP_ID, self.owner_id)
        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)

        base_search_rank = 20

        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary),
            base_search_rank)

        rights_manager.publish_exploration(self.owner, self.EXP_ID)
        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary),
            base_search_rank)

        rating_services.assign_rating_to_exploration(self.owner_id,
                                                     self.EXP_ID, 5)
        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary),
            base_search_rank + 10)

        rating_services.assign_rating_to_exploration(self.user_id_admin,
                                                     self.EXP_ID, 2)
        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertEqual(
            search_services.get_search_rank_from_exp_summary(exp_summary),
            base_search_rank + 8)
    def test_nonhuman_committers_not_counted(self):
        """Test that only human committers are counted as contributors.
        """
        # Create a commit with the system user id.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, feconf.SYSTEM_COMMITTER_ID, title='Original Title')

        # Create commits with all the system user ids
        for system_id in feconf.SYSTEM_USER_IDS:
            exp_services.update_exploration(
                system_id, self.EXP_ID, [{
                    'cmd': 'edit_exploration_property',
                    'property_name': 'title',
                    'new_value': 'Title changed by %s' % system_id
                }], 'Changed title.')

        # Run the job to compute the contributor summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new() # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Check that no system id was added to the exploration's
        # contributor's summary

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        for system_id in feconf.SYSTEM_USER_IDS:
            self.assertNotIn(
                system_id,
                exploration_summary.contributors_summary)
Example #6
0
    def test_repeat_contributors(self):
        """Test that if the same user makes more than one commit that changes
        the content of an exploration, the user is only represented once in the
        list of contributors for that exploration.
        """
        self.signup(self.EMAIL_A, self.USERNAME_A)
        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        # Have one user make two commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Original Title')
        exploration_model = exp_models.ExplorationModel.get(
            self.EXP_ID, strict=True, version=None)
        exploration_model.title = 'New title'
        exploration_model.commit(
            user_a_id, 'Changed title.', [])

        # Run the job to compute the contributor ids.
        job_id = (
            exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new())
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Verify that the length of the contributor list is one, and that
        # the list contains the user who made these commits.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual(
            [user_a_id], exploration_summary.contributor_ids)
Example #7
0
    def test_repeat_contributors(self):
        """Test that if the same user makes more than one commit that changes
        the content of an exploration, the user is only represented once in the
        list of contributors for that exploration.
        """
        self.signup(self.EMAIL_A, self.USERNAME_A)
        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        # Have one user make two commits.
        exploration = self.save_new_valid_exploration(self.EXP_ID,
                                                      user_a_id,
                                                      title='Original Title')
        exploration_model = exp_models.ExplorationModel.get(self.EXP_ID,
                                                            strict=True,
                                                            version=None)
        exploration_model.title = 'New title'
        exploration_model.commit(user_a_id, 'Changed title.', [])

        # Run the job to compute the contributor ids.
        job_id = (
            exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new())
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Verify that the length of the contributor list is one, and that
        # the list contains the user who made these commits.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual([user_a_id], exploration_summary.contributor_ids)
Example #8
0
    def test_nonhuman_committers_not_counted(self):
        """Test that only human committers are counted as contributors."""

        # Create a commit with the system user id.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, feconf.SYSTEM_COMMITTER_ID, title='Original Title')

        # Create commits with all the system user ids.
        for system_id in feconf.SYSTEM_USER_IDS:
            exp_services.update_exploration(system_id, self.EXP_ID, [
                exp_domain.ExplorationChange(
                    {
                        'cmd': 'edit_exploration_property',
                        'property_name': 'title',
                        'new_value': 'Title changed by %s' % system_id
                    })
            ], 'Changed title.')

        # Run the job to compute the contributor summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new()  # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(
            job_id)
        self.process_and_flush_pending_tasks()

        # Check that no system id was added to the exploration's
        # contributor's summary.

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        for system_id in feconf.SYSTEM_USER_IDS:
            self.assertNotIn(system_id,
                             exploration_summary.contributors_summary)
Example #9
0
    def test_contributors_with_only_reverts_not_counted(self):
        """Test that contributors who have only done reverts do not
        have their user id appear in the contributor list.
        """
        # Sign up two users.
        self.signup(self.EMAIL_A, self.USERNAME_A)
        user_a_id = self.get_user_id_from_email(self.EMAIL_A)
        self.signup(self.EMAIL_B, self.USERNAME_B)
        user_b_id = self.get_user_id_from_email(self.EMAIL_B)
        # Have one user make two commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Original Title')
        change_list = [{
            'cmd': exp_domain.CMD_EDIT_EXPLORATION_PROPERTY,
            'property_name': 'title',
            'new_value': 'New title'
        }]
        exp_services.update_exploration(
            user_a_id, self.EXP_ID, change_list, 'Changed title.')

        # Have the second user revert version 2 to version 1
        exp_services.revert_exploration(user_b_id, self.EXP_ID, 2, 1)

        # Run the job to compute the contributor ids.
        job_id = (
            exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new())
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Verify that the committer list does not contain the user
        # who only reverted.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual([user_a_id], exploration_summary.contributor_ids)
Example #10
0
def assign_rating_to_exploration(user_id, exploration_id, new_rating):
    """Records the rating awarded by the user to the exploration in both the
    user-specific data and exploration summary.

    This function validates the exploration id but not the user id.

    Args:
        user_id: str. The id of the user assigning the rating.
        exploration_id: str. The id of the exploration that is
            assigned a rating.
        new_rating: int. Value of assigned rating, should be between
            1 and 5 inclusive.
    """

    if not isinstance(new_rating, int):
        raise ValueError('Expected the rating to be an integer, received %s' %
                         new_rating)

    if new_rating not in ALLOWED_RATINGS:
        raise ValueError('Expected a rating 1-5, received %s.' % new_rating)

    try:
        exp_services.get_exploration_by_id(exploration_id)
    except:
        raise Exception('Invalid exploration id %s' % exploration_id)

    def _update_user_rating():
        """Updates the user rating of the exploration. Returns the old rating
        before updation.
        """
        exp_user_data_model = user_models.ExplorationUserDataModel.get(
            user_id, exploration_id)
        if exp_user_data_model:
            old_rating = exp_user_data_model.rating
        else:
            old_rating = None
            exp_user_data_model = user_models.ExplorationUserDataModel.create(
                user_id, exploration_id)
        exp_user_data_model.rating = new_rating
        exp_user_data_model.rated_on = datetime.datetime.utcnow()
        exp_user_data_model.put()
        return old_rating

    old_rating = transaction_services.run_in_transaction(_update_user_rating)

    exploration_summary = exp_services.get_exploration_summary_by_id(
        exploration_id)
    if not exploration_summary.ratings:
        exploration_summary.ratings = feconf.get_empty_ratings()
    exploration_summary.ratings[str(new_rating)] += 1
    if old_rating:
        exploration_summary.ratings[str(old_rating)] -= 1

    event_services.RateExplorationEventHandler.record(exploration_id, user_id,
                                                      new_rating, old_rating)

    exploration_summary.scaled_average_rating = (
        exp_services.get_scaled_average_rating(exploration_summary.ratings))

    exp_services.save_exploration_summary(exploration_summary)
Example #11
0
    def test_contributors_with_only_reverts_not_counted(self):
        """Test that contributors who have only done reverts do not
        have their user id appear in the contributor list.
        """
        # Sign up two users.
        self.signup(self.EMAIL_A, self.USERNAME_A)
        user_a_id = self.get_user_id_from_email(self.EMAIL_A)
        self.signup(self.EMAIL_B, self.USERNAME_B)
        user_b_id = self.get_user_id_from_email(self.EMAIL_B)
        # Have one user make two commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Original Title')
        exploration_model = exp_models.ExplorationModel.get(
            self.EXP_ID, strict=True, version=None)
        exploration_model.title = 'New title'
        exploration_model.commit(
            user_a_id, 'Changed title.', [])

        # Have the second user revert version 2 to version 1
        exp_services.revert_exploration(user_b_id, self.EXP_ID, 2, 1)

        # Run the job to compute the contributor ids.
        job_id = (
            exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new())
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Verify that the committer list does not contain the user
        # who only reverted.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual([user_a_id], exploration_summary.contributor_ids)
def get_item_similarity(reference_exp_id, compared_exp_id):
    """Returns the ranking of compared_exp_id to reference_exp_id as a
    recommendation. This returns a value between 0.0 to 10.0. A higher value
    indicates the compared_exp is a better recommendation as an exploration to
    start after completing reference_exp.

    Comparison of similarity is based on the similarity of exploration topics,
    whether the explorations have the same language or author. The ranking of
    compared_exp is increased if it is publicized or is newly updated. It
    returns 0.0 if compared_exp is private."""

    try:
        reference_exp_summary = exp_services.get_exploration_summary_by_id(
            reference_exp_id)
    except:
        raise Exception('Invalid reference_exp_id %s' % reference_exp_id)

    try:
        compared_exp_summary = exp_services.get_exploration_summary_by_id(
            compared_exp_id)
    except:
        raise Exception('Invalid compared_exp_id %s' % compared_exp_id)

    similarity_score = 0

    if (compared_exp_summary.status ==
            rights_manager.EXPLORATION_STATUS_PRIVATE):
        return 0
    elif (compared_exp_summary.status ==
            rights_manager.EXPLORATION_STATUS_PUBLICIZED):
        similarity_score += 1

    similarity_score += get_topic_similarity(
        reference_exp_summary.category, compared_exp_summary.category) * 5
    if reference_exp_summary.owner_ids == compared_exp_summary.owner_ids:
        similarity_score += 1
    if (reference_exp_summary.language_code ==
            compared_exp_summary.language_code):
        similarity_score += 2

    time_now = datetime.datetime.utcnow()
    time_delta_days = int(
        (time_now - compared_exp_summary.exploration_model_last_updated).days)
    if time_delta_days <= 7:
        similarity_score += 1

    return similarity_score
Example #13
0
    def test_rating_assignation(self):
        """Check ratings are correctly assigned to an exploration."""

        exp_services.save_new_exploration(
            self.EXP_ID,
            exp_domain.Exploration.create_default_exploration(self.EXP_ID))

        self.assertEqual(
            rating_services.get_overall_ratings_for_exploration(self.EXP_ID),
            {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0})

        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertEqual(
            exp_summary.scaled_average_rating, 0)

        self.assertEqual(
            rating_services.get_user_specific_rating_for_exploration(
                self.USER_ID_1, self.EXP_ID), None)

        rating_services.assign_rating_to_exploration(
            self.USER_ID_1, self.EXP_ID, 2)
        rating_services.assign_rating_to_exploration(
            self.USER_ID_2, self.EXP_ID, 4)
        rating_services.assign_rating_to_exploration(
            self.USER_ID_1, self.EXP_ID, 3)

        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertAlmostEqual(
            exp_summary.scaled_average_rating, 1.5667471839848, places=4)

        self.assertEqual(
            rating_services.get_user_specific_rating_for_exploration(
                self.USER_ID_1, self.EXP_ID), 3)
        self.assertEqual(
            rating_services.get_user_specific_rating_for_exploration(
                self.USER_ID_2, self.EXP_ID), 4)
        self.assertEqual(
            rating_services.get_overall_ratings_for_exploration(self.EXP_ID),
            {'1': 0, '2': 0, '3': 1, '4': 1, '5': 0})

        rating_services.assign_rating_to_exploration(
            self.USER_ID_1, self.EXP_ID, 4)

        self.assertEqual(
            rating_services.get_overall_ratings_for_exploration(self.EXP_ID),
            {'1': 0, '2': 0, '3': 0, '4': 2, '5': 0})
Example #14
0
    def map(item):
        if item.deleted:
            return

        summary = exp_services.get_exploration_summary_by_id(item.id)
        summary.contributors_summary = (
            exp_services.compute_exploration_contributors_summary(item.id))
        exp_services.save_exploration_summary(summary)
Example #15
0
    def map(item):
        if item.deleted:
            return

        summary = exp_services.get_exploration_summary_by_id(item.id)
        summary.contributors_summary = (
            exp_services.compute_exploration_contributors_summary(item.id))
        exp_services.save_exploration_summary(summary)
    def test_rating_assignation(self):
        """Check ratings are correctly assigned to an exploration"""

        exp_services.save_new_exploration(
            self.EXP_ID,
            exp_domain.Exploration.create_default_exploration(self.EXP_ID))

        self.assertEqual(
            rating_services.get_overall_ratings_for_exploration(self.EXP_ID),
            {'1': 0, '2': 0, '3': 0, '4': 0, '5': 0})

        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertEqual(
            exp_summary.scaled_average_rating, 0)

        self.assertEqual(
            rating_services.get_user_specific_rating_for_exploration(
                self.USER_ID_1, self.EXP_ID), None)

        rating_services.assign_rating_to_exploration(
            self.USER_ID_1, self.EXP_ID, 2)
        rating_services.assign_rating_to_exploration(
            self.USER_ID_2, self.EXP_ID, 4)
        rating_services.assign_rating_to_exploration(
            self.USER_ID_1, self.EXP_ID, 3)

        exp_summary = exp_services.get_exploration_summary_by_id(self.EXP_ID)
        self.assertAlmostEqual(
            exp_summary.scaled_average_rating, 1.5667471839848, places=4)

        self.assertEqual(
            rating_services.get_user_specific_rating_for_exploration(
                self.USER_ID_1, self.EXP_ID), 3)
        self.assertEqual(
            rating_services.get_user_specific_rating_for_exploration(
                self.USER_ID_2, self.EXP_ID), 4)
        self.assertEqual(
            rating_services.get_overall_ratings_for_exploration(self.EXP_ID),
            {'1': 0, '2': 0, '3': 1, '4': 1, '5': 0})

        rating_services.assign_rating_to_exploration(
            self.USER_ID_1, self.EXP_ID, 4)

        self.assertEqual(
            rating_services.get_overall_ratings_for_exploration(self.EXP_ID),
            {'1': 0, '2': 0, '3': 0, '4': 2, '5': 0})
Example #17
0
def assign_rating_to_exploration(user_id, exploration_id, new_rating):
    """Records the rating awarded by the user to the exploration in both the
    user-specific data and exploration summary.

    This function validates the exploration id but not the user id.

    Args:
        user_id: str. The id of the user assigning the rating.
        exploration_id: str. The id of the exploration that is
            assigned a rating.
        new_rating: int. Value of assigned rating, should be between
            1 and 5 inclusive.
    """

    if not isinstance(new_rating, int):
        raise ValueError(
            'Expected the rating to be an integer, received %s' % new_rating)

    if new_rating not in ALLOWED_RATINGS:
        raise ValueError('Expected a rating 1-5, received %s.' % new_rating)

    try:
        exp_services.get_exploration_by_id(exploration_id)
    except:
        raise Exception('Invalid exploration id %s' % exploration_id)

    def _update_user_rating():
        exp_user_data_model = user_models.ExplorationUserDataModel.get(
            user_id, exploration_id)
        if exp_user_data_model:
            old_rating = exp_user_data_model.rating
        else:
            old_rating = None
            exp_user_data_model = user_models.ExplorationUserDataModel.create(
                user_id, exploration_id)
        exp_user_data_model.rating = new_rating
        exp_user_data_model.rated_on = datetime.datetime.utcnow()
        exp_user_data_model.put()
        return old_rating
    old_rating = transaction_services.run_in_transaction(_update_user_rating)

    exploration_summary = exp_services.get_exploration_summary_by_id(
        exploration_id)
    if not exploration_summary.ratings:
        exploration_summary.ratings = feconf.get_empty_ratings()
    exploration_summary.ratings[str(new_rating)] += 1
    if old_rating:
        exploration_summary.ratings[str(old_rating)] -= 1

    event_services.RateExplorationEventHandler.record(
        exploration_id, user_id, new_rating, old_rating)

    exploration_summary.scaled_average_rating = (
        exp_services.get_scaled_average_rating(
            exploration_summary.ratings))

    exp_services.save_exploration_summary(exploration_summary)
Example #18
0
    def _handle_incoming_event(cls, active_realtime_layer, event_type, *args):
        exp_id = args[0]

        def _refresh_average_ratings(user_id, rating, old_rating):
            realtime_class = cls._get_realtime_datastore_class()
            realtime_model_id = realtime_class.get_realtime_id(
                active_realtime_layer, user_id)

            model = realtime_class.get(realtime_model_id, strict=False)
            if model is None:
                realtime_class(
                    id=realtime_model_id, average_ratings=rating,
                    num_ratings=1, realtime_layer=active_realtime_layer).put()
            else:
                num_ratings = model.num_ratings
                average_ratings = model.average_ratings
                num_ratings += 1
                if average_ratings is not None:
                    sum_of_ratings = (
                        average_ratings * (num_ratings - 1) + rating)
                    if old_rating is not None:
                        sum_of_ratings -= old_rating
                        num_ratings -= 1
                    model.average_ratings = sum_of_ratings/(num_ratings * 1.0)
                else:
                    model.average_ratings = rating
                model.num_ratings = num_ratings
                model.put()

        def _increment_total_plays_count(user_id):
            realtime_class = cls._get_realtime_datastore_class()
            realtime_model_id = realtime_class.get_realtime_id(
                active_realtime_layer, user_id)

            model = realtime_class.get(realtime_model_id, strict=False)
            if model is None:
                realtime_class(
                    id=realtime_model_id, total_plays=1,
                    realtime_layer=active_realtime_layer).put()
            else:
                model.total_plays += 1
                model.put()

        exp_summary = exp_services.get_exploration_summary_by_id(exp_id)
        if exp_summary:
            for user_id in exp_summary.owner_ids:
                if event_type == feconf.EVENT_TYPE_START_EXPLORATION:
                    transaction_services.run_in_transaction(
                        _increment_total_plays_count, user_id)

                elif event_type == feconf.EVENT_TYPE_RATE_EXPLORATION:
                    rating = args[2]
                    old_rating = args[3]
                    transaction_services.run_in_transaction(
                        _refresh_average_ratings, user_id, rating, old_rating)
Example #19
0
def get_overall_ratings_for_exploration(exploration_id):
    """Fetches all ratings for an exploration.

    Args:
        exploration_id: str. The id of the exploration.

    Returns:
        a dict whose keys are '1', '2', '3', '4', '5' and whose
        values are nonnegative integers representing the frequency counts
        of each rating.
    """
    exp_summary = exp_services.get_exploration_summary_by_id(exploration_id)
    return exp_summary.ratings
Example #20
0
def get_overall_ratings_for_exploration(exploration_id):
    """Fetches all ratings for an exploration.

    Args:
        exploration_id: str. The id of the exploration.

    Returns:
        a dict whose keys are '1', '2', '3', '4', '5' and whose
        values are nonnegative integers representing the frequency counts
        of each rating.
    """
    exp_summary = exp_services.get_exploration_summary_by_id(exploration_id)
    return exp_summary.ratings
Example #21
0
def assign_rating_to_exploration(user_id, exploration_id, new_rating):
    """Records the rating awarded by the user to the exploration in both the
    user-specific data and exploration summary.

    It validates the exploration id but not the user id.

     - 'new_rating' should be an integer between 1 and 5
    """

    if not isinstance(new_rating, int):
        raise ValueError('Expected the rating to be an integer, received %s' %
                         new_rating)

    if new_rating not in ALLOWED_RATINGS:
        raise ValueError('Expected a rating 1-5, received %s.' % new_rating)

    try:
        exp_services.get_exploration_by_id(exploration_id)
    except:
        raise Exception('Invalid exploration id %s' % exploration_id)

    def _update_user_rating():
        exp_user_data_model = user_models.ExplorationUserDataModel.get(
            user_id, exploration_id)
        if exp_user_data_model:
            old_rating = exp_user_data_model.rating
        else:
            old_rating = None
            exp_user_data_model = user_models.ExplorationUserDataModel.create(
                user_id, exploration_id)
        exp_user_data_model.rating = new_rating
        exp_user_data_model.rated_on = datetime.datetime.utcnow()
        exp_user_data_model.put()
        return old_rating

    old_rating = transaction_services.run_in_transaction(_update_user_rating)

    exploration_summary = exp_services.get_exploration_summary_by_id(
        exploration_id)
    if not exploration_summary.ratings:
        exploration_summary.ratings = feconf.get_empty_ratings()
    exploration_summary.ratings[str(new_rating)] += 1
    if old_rating:
        exploration_summary.ratings[str(old_rating)] -= 1

    exploration_summary.scaled_average_rating = (
        exp_services.get_scaled_average_rating(exploration_summary.ratings))

    exp_services.save_exploration_summary(exploration_summary)
Example #22
0
    def map(item):
        if item.deleted:
            return

        exploration_impact_score = (
            UserImpactMRJobManager._get_exp_impact_score(item.id))

        if exploration_impact_score > 0:
            # Get exploration summary and contributor ids,
            # yield for each contributor.
            exploration_summary = exp_services.get_exploration_summary_by_id(
                item.id)
            contributor_ids = exploration_summary.contributor_ids
            for contributor_id in contributor_ids:
                yield (contributor_id, exploration_impact_score)
    def map(item):
        if item.deleted:
            return

        exploration_impact_score = (
            UserImpactMRJobManager._get_exp_impact_score(item.id))

        if exploration_impact_score > 0:
            # Get exploration summary and contributor ids,
            # yield for each contributor.
            exploration_summary = exp_services.get_exploration_summary_by_id(
                item.id)
            contributor_ids = exploration_summary.contributor_ids
            for contributor_id in contributor_ids:
                yield (contributor_id, exploration_impact_score)
Example #24
0
    def test_contributors_for_valid_contribution(self):
        """Test that if only one commit is made, that the contributor
        list consists of that contributor's user id.
        """
        self.signup(self.EMAIL_A, self.USERNAME_A)
        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        exploration = self.save_new_valid_exploration(self.EXP_ID, user_a_id)
        job_id = (
            exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new())
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual([user_a_id], exploration_summary.contributor_ids)
Example #25
0
    def map(item):
        if item.deleted:
            return

        exponent = 2.0/3

        # Get average rating and value per user
        total_rating = 0
        for ratings_value in item.ratings:
            total_rating += item.ratings[ratings_value] * int(ratings_value)
        if not sum(item.ratings.itervalues()):
            return
        average_rating = total_rating / sum(item.ratings.itervalues())
        value_per_user = average_rating - 2
        if value_per_user <= 0:
            return

        statistics = (
            stats_jobs_continuous.StatisticsAggregator.get_statistics(
                item.id, stats_jobs_continuous.VERSION_ALL))
        answer_count = 0
        # Find number of users per state (card), and subtract no answer
        # This will not count people who have been back to a state twice
        # but did not give an answer the second time, but is probably the
        # closest we can get with current statistics to "number of users
        # who gave an answer" since it is "number of users who always gave
        # an answer".
        for state_name in statistics['state_hit_counts']:
            state_stats = statistics['state_hit_counts'][state_name]
            first_entry_count = state_stats.get('first_entry_count', 0)
            no_answer_count = state_stats.get('no_answer_count', 0)
            answer_count += first_entry_count - no_answer_count
        # Turn answer count into reach
        reach = answer_count**exponent

        exploration_summary = exp_services.get_exploration_summary_by_id(
            item.id)
        contributors = exploration_summary.contributors_summary
        total_commits = sum(contributors.itervalues())
        if total_commits == 0:
            return
        for contrib_id in contributors:
            # Find fractional contribution for each contributor
            contribution = contributors[contrib_id] / float(total_commits)
            # Find score for this specific exploration
            exploration_impact_score = value_per_user * reach * contribution
            yield (contrib_id, exploration_impact_score)
Example #26
0
    def test_contributors_for_valid_contribution(self):
        """Test that if only one commit is made, that the contributor
        list consists of that contributor's user id.
        """
        self.signup(self.EMAIL_A, self.USERNAME_A)
        self.user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.user_a_id)
        job_id = exp_jobs_one_off.ExpSummariesContributorsOneOffJob.create_new()
        exp_jobs_one_off.ExpSummariesContributorsOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual(
            [self.user_a_id], exploration_summary.contributor_ids)
Example #27
0
    def test_contributors_with_only_reverts_not_included(self):
        """Test that if only reverts are made by contributor then the
        contributions summary shouldn’t contain that contributor’s ID.
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)
        user_b_id = self.get_user_id_from_email(self.EMAIL_B)

        # Let USER A make three commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Exploration Title 1')

        exp_services.update_exploration(user_a_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New Exploration Title'
            })
        ], 'Changed title.')
        exp_services.update_exploration(user_a_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'New Objective'
            })
        ], 'Changed Objective.')

        # Let the second user revert version 3 to version 2.
        exp_services.revert_exploration(user_b_id, self.EXP_ID, 3, 2)

        # Run the job to compute the contributors summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new()  # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(
            job_id)
        self.process_and_flush_pending_tasks()

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        # Check that the contributors_summary does not contains user_b_id.
        self.assertNotIn(user_b_id, exploration_summary.contributors_summary)

        # Check that the User A has only 2 commits after user b has reverted
        # to version 2.
        self.assertEquals(2, exploration_summary.contributors_summary[user_a_id])  # pylint: disable=line-too-long
Example #28
0
def assign_rating(user_id, exploration_id, new_rating):
    """Records the rating awarded by the user to the exploration in both the
    user-specific data and exploration summary.

    It validates the exploration id but not the user id.

     - 'new_rating' should be an integer between 1 and 5
    """

    if not isinstance(new_rating, int):
        raise ValueError(
            'Expected the rating to be an integer, received %s' % new_rating)

    ALLOWED_RATINGS = [1, 2, 3, 4, 5]
    if new_rating not in ALLOWED_RATINGS:
        raise ValueError('Expected a rating 1-5, received: %s.' % new_rating)

    try:
        exp_services.get_exploration_by_id(exploration_id)
    except:
        raise Exception('Invalid exploration id %s' % exploration_id)

    def _update_user_rating():
        exp_user_data_model = user_models.ExplorationUserDataModel.get(
            user_id, exploration_id)
        if exp_user_data_model:
            old_rating = exp_user_data_model.rating
        else:
            old_rating = None
            exp_user_data_model = user_models.ExplorationUserDataModel.create(
                user_id, exploration_id)
        exp_user_data_model.rating = new_rating
        exp_user_data_model.rated_on = datetime.datetime.utcnow()
        exp_user_data_model.put()
        return old_rating
    old_rating = transaction_services.run_in_transaction(_update_user_rating)

    exploration_summary = exp_services.get_exploration_summary_by_id(
        exploration_id)
    if not exploration_summary.ratings:
        exploration_summary.ratings = feconf.get_empty_ratings()
    exploration_summary.ratings[str(new_rating)] += 1
    if old_rating:
        exploration_summary.ratings[str(old_rating)] -= 1
    exp_services.save_exploration_summary(exploration_summary)
Example #29
0
    def test_reverts_not_counted(self):
        """Test that if both non-revert commits and revert are
        made by contributor then the contributions summary shows
        only non-revert commits for that contributor. However,
        the commits made after the version to which we have reverted
        shouldn't be counted either.
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        # Let USER A make 3 non-revert commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Exploration Title')
        exp_services.update_exploration(user_a_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New Exploration Title'
            })
        ], 'Changed title.')
        exp_services.update_exploration(user_a_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'New Objective'
            })
        ], 'Changed Objective.')

        # Let USER A revert version 3 to version 2.
        exp_services.revert_exploration(user_a_id, self.EXP_ID, 3, 2)

        # Run the job to compute the contributor summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new()  # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(
            job_id)
        self.process_and_flush_pending_tasks()

        # Check that USER A's number of contributions is equal to 2.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual(2,
                         exploration_summary.contributors_summary[user_a_id])
    def test_contributors_with_only_reverts_not_included(self):
        """Test that if only reverts are made by contributor then the
        contributions summary shouldn’t contain that contributor’s ID
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)
        user_b_id = self.get_user_id_from_email(self.EMAIL_B)

        # Let USER A make three commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Exploration Title 1')

        exp_services.update_exploration(
            user_a_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New Exploration Title'
            }], 'Changed title.')
        exp_services.update_exploration(
            user_a_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'New Objective'
            }], 'Changed Objective.')

        # Let the second user revert version 3 to version 2
        exp_services.revert_exploration(user_b_id, self.EXP_ID, 3, 2)

        # Run the job to compute the contributors summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new() # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        # Check that the contributors_summary does not contains user_b_id
        self.assertNotIn(user_b_id, exploration_summary.contributors_summary)

        # Check that the User A has only 2 commits after user b has reverted
        # to version 2
        self.assertEquals(2, exploration_summary.contributors_summary[user_a_id]) # pylint: disable=line-too-long
Example #31
0
    def test_contributors_for_valid_nonrevert_contribution(self):
        """Test that if only non-revert commits are made by
        contributor then the contributions summary shows same
        exact number of commits for that contributor's ID.
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        # Let USER A make three commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Exploration Title')

        exp_services.update_exploration(user_a_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New Exploration Title'
            })
        ], 'Changed title.')

        exp_services.update_exploration(user_a_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'New Objective'
            })
        ], 'Changed Objective.')

        # Run the job to compute contributors summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new()  # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(
            job_id)
        self.process_and_flush_pending_tasks()

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        self.assertEqual(3,
                         exploration_summary.contributors_summary[user_a_id])
    def test_reverts_not_counted(self):
        """Test that if both non-revert commits and revert are
        made by contributor then the contributions summary shows
        only non-revert commits for that contributor. However,
        the commits made after the version to which we have reverted
        shouldn't be counted either.
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        # Let USER A make 3 non-revert commits
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Exploration Title')
        exp_services.update_exploration(
            user_a_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New Exploration Title'
            }], 'Changed title.')
        exp_services.update_exploration(
            user_a_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'New Objective'
            }], 'Changed Objective.')

        # Let USER A revert version 3 to version 2
        exp_services.revert_exploration(user_a_id, self.EXP_ID, 3, 2)

        # Run the job to compute the contributor summary.
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new() # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        # Check that USER A's number of contributions is equal to 2
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual(2, exploration_summary.contributors_summary[user_a_id])
    def test_contributors_for_valid_nonrevert_contribution(self):
        """Test that if only non-revert commits are made by
        contributor then the contributions summary shows same
        exact number of commits for that contributor's ID
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        # Let USER A make three commits.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, user_a_id, title='Exploration Title')

        exp_services.update_exploration(
            user_a_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New Exploration Title'
            }], 'Changed title.')

        exp_services.update_exploration(
            user_a_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'New Objective'
            }], 'Changed Objective.')

        # Run the job to compute contributors summary
        job_id = exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.create_new() # pylint: disable=line-too-long
        exp_jobs_one_off.ExplorationContributorsSummaryOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        self.assertEqual(
            3, exploration_summary.contributors_summary[user_a_id])
Example #34
0
    def map(item):
        """Implements the map function (generator).
        Computes exploration data for every contributor and owner of the
        exploration.

        Args:
            item: ExpSummaryModel. An instance of ExpSummaryModel.

        Yields:
            This function may yield as many times as appropriate 2-tuples in the
            format (str, dict), where
            - str. The unique ID of the user.
            - dict: A dict that includes entries for all the explorations that
                this user contributes to or owns. Each entry has the following
                keys:
                - 'exploration_impact_score': float. The impact score of all the
                    explorations contributed to by the user.
                - 'total_plays_for_owned_exp': int. Total plays of all
                    explorations owned by the user.
                - 'average_rating_for_owned_exp': float. Average of average
                    ratings of all explorations owned by the user.
                - 'num_ratings_for_owned_exp': int. Total number of ratings of
                    all explorations owned by the user.
        """
        if item.deleted:
            return

        exponent = 2.0 / 3

        # This is set to False only when the exploration impact score is not
        # valid to be calculated.
        calculate_exploration_impact_score = True

        # Get average rating and value per user.
        total_rating = 0
        for ratings_value in item.ratings:
            total_rating += item.ratings[ratings_value] * int(ratings_value)
        sum_of_ratings = sum(item.ratings.itervalues())

        average_rating = ((total_rating /
                           sum_of_ratings) if sum_of_ratings else None)

        if average_rating is not None:
            value_per_user = average_rating - 2
            if value_per_user <= 0:
                calculate_exploration_impact_score = False
        else:
            calculate_exploration_impact_score = False

        exploration_stats = stats_services.get_exploration_stats(
            item.id, item.version)
        # For each state, find the number of first entries to the state.
        # This is considered to be approximately equal to the number of
        # users who answered the state because very few users enter a state
        # and leave without answering anything at all.
        answer_count = exploration_stats.get_sum_of_first_hit_counts()
        num_starts = exploration_stats.num_starts

        # Turn answer count into reach.
        reach = answer_count**exponent

        exploration_summary = exp_services.get_exploration_summary_by_id(
            item.id)
        contributors = exploration_summary.contributors_summary
        total_commits = sum(contributors.itervalues())
        if total_commits == 0:
            calculate_exploration_impact_score = False

        mapped_owner_ids = []
        for contrib_id in contributors:
            exploration_data = {}

            # Set the value of exploration impact score only if it needs to be
            # calculated.
            if calculate_exploration_impact_score:
                # Find fractional contribution for each contributor.
                contribution = (contributors[contrib_id] /
                                float(total_commits))

                # Find score for this specific exploration.
                exploration_data.update({
                    'exploration_impact_score':
                    (value_per_user * reach * contribution)
                })

            # if the user is an owner for the exploration, then update dict with
            # 'average ratings' and 'total plays' as well.
            if contrib_id in exploration_summary.owner_ids:
                mapped_owner_ids.append(contrib_id)
                # Get num starts (total plays) for the exploration.
                exploration_data.update({
                    'total_plays_for_owned_exp':
                    num_starts,
                })
                # Update data with average rating only if it is not None.
                if average_rating is not None:
                    exploration_data.update({
                        'average_rating_for_owned_exp':
                        average_rating,
                        'num_ratings_for_owned_exp':
                        sum_of_ratings
                    })
            yield (contrib_id, exploration_data)

        for owner_id in exploration_summary.owner_ids:
            if owner_id not in mapped_owner_ids:
                mapped_owner_ids.append(owner_id)
                # Get num starts (total plays) for the exploration.
                exploration_data = {
                    'total_plays_for_owned_exp': num_starts,
                }
                # Update data with average rating only if it is not None.
                if average_rating is not None:
                    exploration_data.update({
                        'average_rating_for_owned_exp':
                        average_rating,
                        'num_ratings_for_owned_exp':
                        sum_of_ratings
                    })
                yield (owner_id, exploration_data)
Example #35
0
    def _handle_incoming_event(cls, active_realtime_layer, event_type, *args):
        """Records incoming events in the given realtime layer.

        Args:
            active_realtime_layer: int. The currently active realtime datastore
                layer.
            event_type: str. The event triggered by a student. For example, when
                a student starts an exploration, an event of type `start` is
                triggered and the total play count is incremented. If he/she
                rates an exploration, an event of type `rate` is triggered and
                average rating of the realtime model is refreshed.
            *args:
                If event_type is 'start', then this is a 1-element list with
                following entry:
                - str. The ID of the exploration currently being played.
                If event_type is 'rate_exploration', then this is a 3-element
                list with following entries:
                - str. The ID of the exploration currently being played.
                - float. The rating given by user to the exploration.
                - float. The old rating of the exploration, before it is
                    refreshed.
        """
        exp_id = args[0]

        def _refresh_average_ratings(user_id, rating, old_rating):
            realtime_class = cls._get_realtime_datastore_class()
            realtime_model_id = realtime_class.get_realtime_id(
                active_realtime_layer, user_id)

            model = realtime_class.get(realtime_model_id, strict=False)
            if model is None:
                realtime_class(id=realtime_model_id,
                               average_ratings=rating,
                               num_ratings=1,
                               realtime_layer=active_realtime_layer).put()
            else:
                num_ratings = model.num_ratings
                average_ratings = model.average_ratings
                num_ratings += 1
                if average_ratings is not None:
                    sum_of_ratings = (average_ratings * (num_ratings - 1) +
                                      rating)
                    if old_rating is not None:
                        sum_of_ratings -= old_rating
                        num_ratings -= 1
                    model.average_ratings = sum_of_ratings / (num_ratings *
                                                              1.0)
                else:
                    model.average_ratings = rating
                model.num_ratings = num_ratings
                model.put()

        def _increment_total_plays_count(user_id):
            realtime_class = cls._get_realtime_datastore_class()
            realtime_model_id = realtime_class.get_realtime_id(
                active_realtime_layer, user_id)

            model = realtime_class.get(realtime_model_id, strict=False)
            if model is None:
                realtime_class(id=realtime_model_id,
                               total_plays=1,
                               realtime_layer=active_realtime_layer).put()
            else:
                model.total_plays += 1
                model.put()

        exp_summary = exp_services.get_exploration_summary_by_id(exp_id)
        if exp_summary:
            for user_id in exp_summary.owner_ids:
                if event_type == feconf.EVENT_TYPE_START_EXPLORATION:
                    transaction_services.run_in_transaction(
                        _increment_total_plays_count, user_id)

                elif event_type == feconf.EVENT_TYPE_RATE_EXPLORATION:
                    rating = args[2]
                    old_rating = args[3]
                    transaction_services.run_in_transaction(
                        _refresh_average_ratings, user_id, rating, old_rating)
Example #36
0
    def map(item):
        """Implements the map function (generator).
        Computes exploration data for every contributor and owner of the
        exploration.

        Args:
            item: ExpSummaryModel. An instance of ExpSummaryModel.

        Yields:
            This function may yield as many times as appropriate 2-tuples in the
            format (str, dict), where
            - str. The unique ID of the user.
            - dict: A dict that includes entries for all the explorations that
                this user contributes to or owns. Each entry has the following
                keys:
                - 'exploration_impact_score': float. The impact score of all the
                    explorations contributed to by the user.
                - 'total_plays_for_owned_exp': int. Total plays of all
                    explorations owned by the user.
                - 'average_rating_for_owned_exp': float. Average of average
                    ratings of all explorations owned by the user.
                - 'num_ratings_for_owned_exp': int. Total number of ratings of
                    all explorations owned by the user.
        """
        if item.deleted:
            return

        exponent = 2.0 / 3

        # This is set to False only when the exploration impact score is not
        # valid to be calculated.
        calculate_exploration_impact_score = True

        # Get average rating and value per user
        total_rating = 0
        for ratings_value in item.ratings:
            total_rating += item.ratings[ratings_value] * int(ratings_value)
        sum_of_ratings = sum(item.ratings.itervalues())

        average_rating = ((total_rating /
                           sum_of_ratings) if sum_of_ratings else None)

        if average_rating is not None:
            value_per_user = average_rating - 2
            if value_per_user <= 0:
                calculate_exploration_impact_score = False
        else:
            calculate_exploration_impact_score = False

        statistics = (
            stats_jobs_continuous.StatisticsAggregator.get_statistics(
                item.id, stats_jobs_continuous.VERSION_ALL))
        answer_count = 0
        # Find number of users per state (card), and subtract no answer
        # This will not count people who have been back to a state twice
        # but did not give an answer the second time, but is probably the
        # closest we can get with current statistics to "number of users
        # who gave an answer" since it is "number of users who always gave
        # an answer".
        for state_name in statistics['state_hit_counts']:
            state_stats = statistics['state_hit_counts'][state_name]
            first_entry_count = state_stats.get('first_entry_count', 0)
            no_answer_count = state_stats.get('no_answer_count', 0)
            answer_count += first_entry_count - no_answer_count
        # Turn answer count into reach
        reach = answer_count**exponent

        exploration_summary = exp_services.get_exploration_summary_by_id(
            item.id)
        contributors = exploration_summary.contributors_summary
        total_commits = sum(contributors.itervalues())
        if total_commits == 0:
            calculate_exploration_impact_score = False

        mapped_owner_ids = []
        for contrib_id in contributors:
            exploration_data = {}

            # Set the value of exploration impact score only if it needs to be
            # calculated.
            if calculate_exploration_impact_score:
                # Find fractional contribution for each contributor
                contribution = (contributors[contrib_id] /
                                float(total_commits))

                # Find score for this specific exploration
                exploration_data.update({
                    'exploration_impact_score':
                    (value_per_user * reach * contribution)
                })

            # if the user is an owner for the exploration, then update dict with
            # 'average ratings' and 'total plays' as well.
            if contrib_id in exploration_summary.owner_ids:
                mapped_owner_ids.append(contrib_id)
                # Get num starts (total plays) for the exploration
                exploration_data.update({
                    'total_plays_for_owned_exp':
                    (statistics['start_exploration_count']),
                })
                # Update data with average rating only if it is not None.
                if average_rating is not None:
                    exploration_data.update({
                        'average_rating_for_owned_exp':
                        average_rating,
                        'num_ratings_for_owned_exp':
                        sum_of_ratings
                    })
            yield (contrib_id, exploration_data)

        for owner_id in exploration_summary.owner_ids:
            if owner_id not in mapped_owner_ids:
                mapped_owner_ids.append(owner_id)
                # Get num starts (total plays) for the exploration
                exploration_data = {
                    'total_plays_for_owned_exp':
                    (statistics['start_exploration_count']),
                }
                # Update data with average rating only if it is not None.
                if average_rating is not None:
                    exploration_data.update({
                        'average_rating_for_owned_exp':
                        average_rating,
                        'num_ratings_for_owned_exp':
                        sum_of_ratings
                    })
                yield (owner_id, exploration_data)
Example #37
0
    def map(item):
        if item.deleted:
            return

        exponent = 2.0/3

        # This is set to False only when the exploration impact score is not
        # valid to be calculated.
        calculate_exploration_impact_score = True

        # Get average rating and value per user
        total_rating = 0
        for ratings_value in item.ratings:
            total_rating += item.ratings[ratings_value] * int(ratings_value)
        sum_of_ratings = sum(item.ratings.itervalues())

        average_rating = ((total_rating / sum_of_ratings) if sum_of_ratings
                          else None)

        if average_rating is not None:
            value_per_user = average_rating - 2
            if value_per_user <= 0:
                calculate_exploration_impact_score = False
        else:
            calculate_exploration_impact_score = False

        statistics = (
            stats_jobs_continuous.StatisticsAggregator.get_statistics(
                item.id, stats_jobs_continuous.VERSION_ALL))
        answer_count = 0
        # Find number of users per state (card), and subtract no answer
        # This will not count people who have been back to a state twice
        # but did not give an answer the second time, but is probably the
        # closest we can get with current statistics to "number of users
        # who gave an answer" since it is "number of users who always gave
        # an answer".
        for state_name in statistics['state_hit_counts']:
            state_stats = statistics['state_hit_counts'][state_name]
            first_entry_count = state_stats.get('first_entry_count', 0)
            no_answer_count = state_stats.get('no_answer_count', 0)
            answer_count += first_entry_count - no_answer_count
        # Turn answer count into reach
        reach = answer_count**exponent

        exploration_summary = exp_services.get_exploration_summary_by_id(
            item.id)
        contributors = exploration_summary.contributors_summary
        total_commits = sum(contributors.itervalues())
        if total_commits == 0:
            calculate_exploration_impact_score = False

        mapped_owner_ids = []
        for contrib_id in contributors:
            exploration_data = {}

            # Set the value of exploration impact score only if it needs to be
            # calculated.
            if calculate_exploration_impact_score:
                # Find fractional contribution for each contributor
                contribution = (
                    contributors[contrib_id] / float(total_commits))

                # Find score for this specific exploration
                exploration_data.update({
                    'exploration_impact_score': (
                        value_per_user * reach * contribution)
                })

            # if the user is an owner for the exploration, then update dict with
            # 'average ratings' and 'total plays' as well.
            if contrib_id in exploration_summary.owner_ids:
                mapped_owner_ids.append(contrib_id)
                # Get num starts (total plays) for the exploration
                exploration_data.update({
                    'total_plays_for_owned_exp': (
                        statistics['start_exploration_count']),
                })
                # Update data with average rating only if it is not None.
                if average_rating is not None:
                    exploration_data.update({
                        'average_rating_for_owned_exp': average_rating
                    })
            yield (contrib_id, exploration_data)

        for owner_id in exploration_summary.owner_ids:
            if owner_id not in mapped_owner_ids:
                mapped_owner_ids.append(owner_id)
                # Get num starts (total plays) for the exploration
                exploration_data = {
                    'total_plays_for_owned_exp': (
                        statistics['start_exploration_count']),
                }
                # Update data with average rating only if it is not None.
                if average_rating is not None:
                    exploration_data.update({
                        'average_rating_for_owned_exp': average_rating
                    })
                yield (owner_id, exploration_data)
Example #38
0
def get_overall_ratings(exploration_id):
    exp_summary = exp_services.get_exploration_summary_by_id(exploration_id)
    return exp_summary.ratings
Example #39
0
def get_overall_ratings_for_exploration(exploration_id):
    exp_summary = exp_services.get_exploration_summary_by_id(exploration_id)
    return exp_summary.ratings