def test_for_featured_explorations(self):
        """Note that both EXP_ID_1 and EXP_ID_2 are public. However, only
        EXP_ID_2 is featured, so the call to get_featured_explorations() should
        only return [EXP_ID_2].
        """
        activity_services.update_featured_activity_references([
            activity_domain.ActivityReference(
                constants.ACTIVITY_TYPE_EXPLORATION, self.EXP_ID_2)
        ])

        featured_activity_summaries = (
            summary_services.get_featured_activity_summary_dicts([
                constants.DEFAULT_LANGUAGE_CODE]))
        self.assertEqual(len(featured_activity_summaries), 1)
        self.assertDictContainsSubset({
            'status': 'public',
            'thumbnail_bg_color': '#a33f40',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/subjects/Lightbulb.svg',
            'language_code': constants.DEFAULT_LANGUAGE_CODE,
            'id': self.EXP_ID_2,
            'category': 'A category',
            'ratings': feconf.get_empty_ratings(),
            'title': 'A title',
            'num_views': 0,
            'objective': 'An objective'
        }, featured_activity_summaries[0])
    def test_get_displayable_exp_summary_dicts_matching_ids(self):
        # A list of exp_id's are passed in:
        # EXP_ID_1 -- private exploration owned by Albert.
        # EXP_ID_2 -- pubished exploration owned by Albert.
        # EXP_ID_3 -- deleted exploration.
        # EXP_ID_5 -- private exploration owned by Bob.
        # Should only return [EXP_ID_2].

        displayable_summaries = (
            summary_services.get_displayable_exp_summary_dicts_matching_ids(
                [self.EXP_ID_1, self.EXP_ID_2, self.EXP_ID_3, self.EXP_ID_5]))
        expected_summary = {
            'category': u'A category',
            'community_owned': False,
            'id': self.EXP_ID_2,
            'language_code': constants.DEFAULT_LANGUAGE_CODE,
            'num_views': 0,
            'objective': u'An objective',
            'ratings': feconf.get_empty_ratings(),
            'status': 'public',
            'tags': [],
            'thumbnail_bg_color': '#a33f40',
            'thumbnail_icon_url': '/subjects/Lightbulb.svg',
            'title': u'Exploration 2 Albert title',
        }
        self.assertIn('last_updated_msec', displayable_summaries[0])
        self.assertDictContainsSubset(
            expected_summary, displayable_summaries[0])
    def test_get_library_groups(self):
        """The exploration with id '2' is an exploration in the Mathematics
        category. The call to get_library_groups() should return the
        exploration as part of the Mathematics & Statistics group.
        """
        library_groups = summary_services.get_library_groups([])
        expected_exploration_summary_dict = {
            'category': u'Algorithms',
            'community_owned': True,
            'id': '2',
            'language_code': constants.DEFAULT_LANGUAGE_CODE,
            'num_views': 0,
            'objective': u'discover the binary search algorithm',
            'ratings': feconf.get_empty_ratings(),
            'status': u'public',
            'tags': [],
            'title': u'The Lazy Magician',
            'thumbnail_bg_color': '#d0982a',
            'thumbnail_icon_url': '/subjects/Algorithms.svg',
        }
        expected_group = {
            'categories': ['Algorithms', 'Computing', 'Programming'],
            'header_i18n_id': 'I18N_LIBRARY_GROUPS_COMPUTING',
        }

        self.assertEqual(len(library_groups), 1)
        self.assertDictContainsSubset(expected_group, library_groups[0])
        self.assertEqual(
            len(library_groups[0]['activity_summary_dicts']), 1)
        actual_exploration_summary_dict = (
            library_groups[0]['activity_summary_dicts'][0])
        self.assertDictContainsSubset(expected_exploration_summary_dict, (
            actual_exploration_summary_dict))
    def test_for_recently_published_explorations(self):
        """Tests for recently published explorations."""

        self.process_and_flush_pending_tasks()
        recently_published_exploration_summaries = (
            summary_services.get_recently_published_exp_summary_dicts(
                feconf.RECENTLY_PUBLISHED_QUERY_LIMIT_FOR_LIBRARY_PAGE))
        test_summary_1 = {
            'status': 'public',
            'thumbnail_bg_color': '#a33f40',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/subjects/Lightbulb.svg',
            'language_code': constants.DEFAULT_LANGUAGE_CODE,
            'id': self.EXP_ID_1,
            'category': u'A category',
            'ratings': feconf.get_empty_ratings(),
            'title': u'A title',
            'num_views': 0,
            'objective': u'An objective'
        }
        test_summary_2 = {
            'status': 'public',
            'thumbnail_bg_color': '#a33f40',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/subjects/Lightbulb.svg',
            'language_code': constants.DEFAULT_LANGUAGE_CODE,
            'id': self.EXP_ID_2,
            'category': u'A category',
            'ratings': feconf.get_empty_ratings(),
            'title': u'A title',
            'num_views': 0,
            'objective': u'An objective'
        }
        test_summary_3 = {
            'status': 'public',
            'thumbnail_bg_color': '#a33f40',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/subjects/Lightbulb.svg',
            'language_code': constants.DEFAULT_LANGUAGE_CODE,
            'id': self.EXP_ID_3,
            'category': u'A category',
            'ratings': feconf.get_empty_ratings(),
            'title': u'A title',
            'num_views': 0,
            'objective': u'An objective'
        }

        self.assertDictContainsSubset(
            test_summary_3, recently_published_exploration_summaries[0])
        self.assertDictContainsSubset(
            test_summary_1, recently_published_exploration_summaries[1])
        self.assertDictContainsSubset(
            test_summary_2, recently_published_exploration_summaries[2])

        # Test that editing an exploration does not change its
        # 'recently-published' status.
        exp_services.update_exploration(
            self.albert_id, self.EXP_ID_1, [exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New title'
            })], 'Changed title.')
        self.process_and_flush_pending_tasks()

        recently_published_exploration_summaries = (
            summary_services.get_recently_published_exp_summary_dicts(
                feconf.RECENTLY_PUBLISHED_QUERY_LIMIT_FOR_LIBRARY_PAGE))
        self.assertEqual(
            recently_published_exploration_summaries[1]['title'], 'New title')
        self.assertDictContainsSubset(
            test_summary_3, recently_published_exploration_summaries[0])
Example #5
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)

    exploration = exp_fetchers.get_exploration_by_id(exploration_id,
                                                     strict=False)
    if exploration is None:
        raise ValueError('Invalid exploration id %s' % exploration_id)

    @transaction_services.run_in_transaction_wrapper
    def _update_user_rating_transactional():
        """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.update_timestamps()
        exp_user_data_model.put()
        return old_rating

    old_rating = _update_user_rating_transactional()

    exploration_summary = exp_fetchers.get_exploration_summary_by_id(
        exploration_id)
    if not exploration_summary.ratings:
        exploration_summary.ratings = feconf.get_empty_ratings()
    exploration_summary.ratings[python_utils.UNICODE(new_rating)] += 1
    if old_rating:
        exploration_summary.ratings[python_utils.UNICODE(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)