Пример #1
0
def get_summary_of_exploration(exploration):
    """Create ExplorationSummary domain object for a given Exploration
    domain object and return it.
    """
    exp_rights = exp_models.ExplorationRightsModel.get_by_id(exploration.id)
    exp_summary_model = exp_models.ExpSummaryModel.get_by_id(exploration.id)
    if exp_summary_model:
        old_exp_summary = get_exploration_summary_from_model(exp_summary_model)
        ratings = old_exp_summary.ratings or feconf.get_empty_ratings()
    else:
        ratings = feconf.get_empty_ratings()

    exploration_model_last_updated = exploration.last_updated
    exploration_model_created_on = exploration.created_on

    exp_summary = exp_domain.ExplorationSummary(
        exploration.id, exploration.title, exploration.category,
        exploration.objective, exploration.language_code,
        exploration.tags, ratings, exp_rights.status,
        exp_rights.community_owned, exp_rights.owner_ids,
        exp_rights.editor_ids, exp_rights.viewer_ids, exploration.version,
        exploration_model_created_on, exploration_model_last_updated
    )

    return exp_summary
Пример #2
0
    def test_for_featured_explorations(self):
        """Note that EXP_ID_1 is public, and EXP_ID_2 is publicized.
        The call to get_featured_explorations() should only return
        [EXP_ID_2].
        """

        rights_manager.publicize_exploration(self.admin_id, self.EXP_ID_2)

        featured_exploration_summaries = (
            summary_services.get_featured_exploration_summary_dicts([
                feconf.DEFAULT_LANGUAGE_CODE]))
        expected_summary = {
            'status': u'publicized',
            'thumbnail_bg_color': '#a33f40',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/images/subjects/Lightbulb.svg',
            'language_code': feconf.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'
        }
        self.assertDictContainsSubset(
            expected_summary, featured_exploration_summaries[0])
Пример #3
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
        # EXP_ID_2 -- pubished exploration
        # EXP_ID_3 -- deleted exploration
        # 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]))
        expected_summary = {
            'status': u'public',
            'thumbnail_bg_color': '#05a69a',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/images/gallery/thumbnails/Lightbulb.svg',
            'language_code': feconf.DEFAULT_LANGUAGE_CODE,
            'human_readable_contributors_summary': {self.ALBERT_NAME: 2},
            'id': self.EXP_ID_2,
            'category': u'A category',
            'ratings': feconf.get_empty_ratings(),
            'title': u'Exploration 2 Albert title',
            'num_views': 0,
            'objective': u'An objective',
            'contributor_names': [self.ALBERT_NAME]
        }
        self.assertIn('last_updated_msec', displayable_summaries[0])
        self.assertDictContainsSubset(expected_summary,
                                      displayable_summaries[0])
Пример #4
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': feconf.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': self.get_static_asset_url(
                '/images/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))
Пример #5
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
        # EXP_ID_2 -- pubished exploration
        # EXP_ID_3 -- deleted exploration
        # 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]))
        expected_summary = {
            'status': u'public',
            'thumbnail_bg_color': '#05a69a',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/images/gallery/thumbnails/Lightbulb.svg',
            'language_code': feconf.DEFAULT_LANGUAGE_CODE,
            'human_readable_contributors_summary': {
                self.ALBERT_NAME: {
                    'num_commits': 2,
                    'profile_picture_data_url': None
                }
            },
            'id': self.EXP_ID_2,
            'category': u'A category',
            'ratings': feconf.get_empty_ratings(),
            'title': u'Exploration 2 Albert title',
            'num_views': 0,
            'objective': u'An objective'
        }
        self.assertIn('last_updated_msec', displayable_summaries[0])
        self.assertDictContainsSubset(expected_summary,
                                      displayable_summaries[0])
Пример #6
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
        # EXP_ID_2 -- pubished exploration
        # EXP_ID_3 -- deleted exploration
        # 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.assertEqual(len(displayable_summaries), 1)
        self.assertEqual(
            displayable_summaries[0]['id'], self.EXP_ID_2)
        self.assertEqual(
            displayable_summaries[0]['status'],
            rights_manager.ACTIVITY_STATUS_PUBLIC)
        self.assertEqual(
            displayable_summaries[0]['community_owned'], False)
        self.assertEqual(
            displayable_summaries[0]['language_code'],
            feconf.DEFAULT_LANGUAGE_CODE)
        self.assertEqual(
            displayable_summaries[0]['category'], 'A category')
        self.assertEqual(
            displayable_summaries[0]['ratings'], feconf.get_empty_ratings())
        self.assertEqual(
            displayable_summaries[0]['title'], 'Exploration 2 Albert title')
        self.assertEqual(
            displayable_summaries[0]['contributor_names'], [self.ALBERT_NAME])
        self.assertEqual(
            displayable_summaries[0]['objective'], 'An objective')
        self.assertEqual(displayable_summaries[0]['num_views'], 0)
        self.assertIn('last_updated_msec', displayable_summaries[0])
Пример #7
0
    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(
                feconf.ACTIVITY_TYPE_EXPLORATION, self.EXP_ID_2)
        ])

        featured_activity_summaries = (
            summary_services.get_featured_activity_summary_dicts([
                feconf.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': self.get_static_asset_url(
                '/images/subjects/Lightbulb.svg'),
            'language_code': feconf.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])
Пример #8
0
    def test_for_featured_explorations(self):
        """Note that EXP_ID_1 is public, and EXP_ID_2 is publicized.
        The call to get_featured_explorations() should only return
        [EXP_ID_2].
        """

        rights_manager.publicize_exploration(self.admin_id, self.EXP_ID_2)

        featured_exploration_summaries = (
            summary_services.get_featured_exploration_summary_dicts(
                [feconf.DEFAULT_LANGUAGE_CODE]))
        expected_summary = {
            'status': u'publicized',
            'thumbnail_bg_color': '#a33f40',
            'community_owned': False,
            'tags': [],
            'thumbnail_icon_url': '/images/subjects/Lightbulb.svg',
            'language_code': feconf.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'
        }
        self.assertDictContainsSubset(expected_summary,
                                      featured_exploration_summaries[0])
Пример #9
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
        # EXP_ID_2 -- pubished exploration
        # EXP_ID_3 -- deleted exploration
        # 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]
        )
        expected_summary = {
            "status": u"public",
            "thumbnail_bg_color": "#05a69a",
            "community_owned": False,
            "tags": [],
            "thumbnail_icon_url": "/images/gallery/thumbnails/Lightbulb.svg",
            "language_code": feconf.DEFAULT_LANGUAGE_CODE,
            "human_readable_contributors_summary": {self.ALBERT_NAME: 2},
            "id": self.EXP_ID_2,
            "category": u"A category",
            "ratings": feconf.get_empty_ratings(),
            "title": u"Exploration 2 Albert title",
            "num_views": 0,
            "objective": u"An objective",
            "contributor_names": [self.ALBERT_NAME],
        }
        self.assertIn("last_updated_msec", displayable_summaries[0])
        self.assertDictContainsSubset(expected_summary, displayable_summaries[0])
Пример #10
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))
Пример #11
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])
Пример #12
0
    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])
Пример #13
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
        # EXP_ID_2 -- pubished exploration
        # EXP_ID_3 -- deleted exploration
        # 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.assertEqual(len(displayable_summaries), 1)
        self.assertEqual(
            displayable_summaries[0]['id'], self.EXP_ID_2)
        self.assertEqual(
            displayable_summaries[0]['status'],
            rights_manager.ACTIVITY_STATUS_PUBLIC)
        self.assertEqual(
            displayable_summaries[0]['community_owned'], False)
        self.assertEqual(
            displayable_summaries[0]['language_code'],
            feconf.DEFAULT_LANGUAGE_CODE)
        self.assertEqual(
            displayable_summaries[0]['category'], 'A category')
        self.assertEqual(
            displayable_summaries[0]['ratings'], feconf.get_empty_ratings())
        self.assertEqual(
            displayable_summaries[0]['title'], 'Exploration 2 Albert title')
        self.assertEqual(
            displayable_summaries[0]['contributor_names'], [self.ALBERT_NAME])
        self.assertEqual(
            displayable_summaries[0]['objective'], 'An objective')
        self.assertEqual(displayable_summaries[0]['num_views'], 0)
        self.assertIn('last_updated_msec', displayable_summaries[0])
Пример #14
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': feconf.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': self.get_static_asset_url(
                '/images/subjects/Lightbulb.svg'),
            'title': u'Exploration 2 Albert title',
        }
        self.assertIn('last_updated_msec', displayable_summaries[0])
        self.assertDictContainsSubset(expected_summary,
                                      displayable_summaries[0])
Пример #15
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_fetchers.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_fetchers.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)
Пример #16
0
    def save_new_exp_with_states_schema_v34(self, exp_id, user_id, states_dict):
        """Saves a new default exploration with a default version 34 states
        dictionary.

        This function should only be used for creating explorations in tests
        involving migration of datastore explorations that use an old states
        schema version.
        Note that it makes an explicit commit to the datastore instead of using
        the usual functions for updating and creating explorations. This is
        because the latter approach would result in an exploration with the
        *current* states schema version.

        Args:
            exp_id: str. The exploration ID.
            user_id: str. The user_id of the creator.
            states_dict: dict. The dict representation of all the states.
        """
        exp_model = exp_models.ExplorationModel(
            id=exp_id,
            category='category',
            title='title',
            objective='Old objective',
            language_code='en',
            tags=[],
            blurb='',
            author_notes='',
            states_schema_version=34,
            init_state_name=feconf.DEFAULT_INIT_STATE_NAME,
            states=states_dict,
            param_specs={},
            param_changes=[]
        )
        rights_manager.create_new_exploration_rights(exp_id, user_id)

        commit_message = 'New exploration created with title \'title\'.'
        exp_model.commit(
            user_id, commit_message, [{
                'cmd': 'create_new',
                'title': 'title',
                'category': 'category',
            }])
        exp_rights = exp_models.ExplorationRightsModel.get_by_id(exp_id)
        exp_summary_model = exp_models.ExpSummaryModel(
            id=exp_id,
            title='title',
            category='category',
            objective='Old objective',
            language_code='en',
            tags=[],
            ratings=feconf.get_empty_ratings(),
            scaled_average_rating=feconf.EMPTY_SCALED_AVERAGE_RATING,
            status=exp_rights.status,
            community_owned=exp_rights.community_owned,
            owner_ids=exp_rights.owner_ids,
            contributor_ids=[],
            contributors_summary={},
        )
        exp_summary_model.update_timestamps()
        exp_summary_model.put()
Пример #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)
Пример #18
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)
Пример #19
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)
Пример #20
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,
            'human_readable_contributors_summary': {
                self.ALBERT_NAME: {
                    'num_commits':
                    2,
                    'profile_picture_data_url':
                    (user_services.DEFAULT_IDENTICON_DATA_URL)
                }
            },
            'id': self.EXP_ID_2,
            'language_code': feconf.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': '/images/subjects/Lightbulb.svg',
            'title': u'Exploration 2 Albert title',
        }
        self.assertIn('last_updated_msec', displayable_summaries[0])
        self.assertDictContainsSubset(expected_summary,
                                      displayable_summaries[0])
Пример #21
0
    def _run_batch_job_once_and_verify_output(
            self,
            exp_specs,
            default_title='A title',
            default_category='A category',
            default_status=rights_manager.ACTIVITY_STATUS_PUBLIC):
        """Run batch job for creating exploration summaries once and verify its
        output. exp_specs is a list of dicts with exploration specifications.
        Allowed keys are category, status, title. If a key is not specified,
        the default value is used.
        """
        with self.swap(jobs_registry, 'ONE_OFF_JOB_MANAGERS',
                       self.ONE_OFF_JOB_MANAGERS_FOR_TESTS):

            default_spec = {
                'title': default_title,
                'category': default_category,
                'status': default_status
            }

            self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
            self.login(self.ADMIN_EMAIL)
            admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)
            self.set_admins([self.ADMIN_USERNAME])
            admin = user_services.UserActionsInfo(admin_id)

            # Create and delete an exploration (to make sure job handles
            # deleted explorations correctly).
            exp_id = '100'
            self.save_new_valid_exploration(exp_id,
                                            admin_id,
                                            title=default_spec['title'],
                                            category=default_spec['category'])
            exploration = exp_services.get_exploration_by_id(exp_id)
            exp_services.delete_exploration(admin_id, exp_id)

            # Get dummy explorations.
            num_exps = len(exp_specs)
            expected_job_output = {}

            for ind in range(num_exps):
                exp_id = str(ind)
                spec = default_spec
                spec.update(exp_specs[ind])
                self.save_new_valid_exploration(exp_id,
                                                admin_id,
                                                title=spec['title'],
                                                category=spec['category'])
                exploration = exp_services.get_exploration_by_id(exp_id)

                # Publish exploration.
                if spec['status'] == rights_manager.ACTIVITY_STATUS_PUBLIC:
                    rights_manager.publish_exploration(admin, exp_id)

                # Do not include user_id here, so all explorations are not
                # editable for now (will be updated depending on user_id
                # in galleries).
                exp_rights_model = exp_models.ExplorationRightsModel.get(
                    exp_id)

                exploration = exp_services.get_exploration_by_id(exp_id)
                exploration_model_last_updated = exploration.last_updated
                exploration_model_created_on = exploration.created_on
                first_published_msec = (exp_rights_model.first_published_msec)

                # Manually create the expected summary specifying title,
                # category, etc.
                expected_job_output[exp_id] = exp_domain.ExplorationSummary(
                    exp_id, spec['title'], spec['category'],
                    exploration.objective, exploration.language_code,
                    exploration.tags, feconf.get_empty_ratings(),
                    feconf.EMPTY_SCALED_AVERAGE_RATING, spec['status'],
                    exp_rights_model.community_owned,
                    exp_rights_model.owner_ids, exp_rights_model.editor_ids,
                    exp_rights_model.translator_ids,
                    exp_rights_model.viewer_ids, [admin_id], {admin_id: 1},
                    exploration.version, exploration_model_created_on,
                    exploration_model_last_updated, first_published_msec)

                # Note: Calling constructor for fields that are not required
                # and have no default value does not work, because
                # unspecified fields will be empty list in
                # expected_job_output but will be unspecified in
                # actual_job_output.
                if exploration.tags:
                    expected_job_output[exp_id].tags = exploration.tags
                if exp_rights_model.owner_ids:
                    expected_job_output[exp_id].owner_ids = (
                        exp_rights_model.owner_ids)
                if exp_rights_model.editor_ids:
                    expected_job_output[exp_id].editor_ids = (
                        exp_rights_model.editor_ids)
                if exp_rights_model.translator_ids:
                    expected_job_output[exp_id].translator_ids = (
                        exp_rights_model.translator_ids)
                if exp_rights_model.viewer_ids:
                    expected_job_output[exp_id].viewer_ids = (
                        exp_rights_model.viewer_ids)
                if exploration.version:
                    expected_job_output[exp_id].version = (exploration.version)

            # Run batch job.
            job_id = (
                exp_jobs_one_off.ExpSummariesCreationOneOffJob.create_new())
            exp_jobs_one_off.ExpSummariesCreationOneOffJob.enqueue(job_id)
            self.process_and_flush_pending_tasks()

            # Get and check job output.
            actual_job_output = exp_services.get_all_exploration_summaries()
            self.assertEqual(actual_job_output.keys(),
                             expected_job_output.keys())

            # Note: 'exploration_model_last_updated' is not expected to be the
            # same, because it is now read from the version model representing
            # the exploration's history snapshot, and not the ExplorationModel.
            simple_props = [
                'id', 'title', 'category', 'objective', 'language_code',
                'tags', 'ratings', 'status', 'community_owned', 'owner_ids',
                'editor_ids', 'translator_ids', 'viewer_ids',
                'contributor_ids', 'contributors_summary', 'version',
                'exploration_model_created_on'
            ]
            for exp_id in actual_job_output:
                for prop in simple_props:
                    self.assertEqual(
                        getattr(actual_job_output[exp_id], prop),
                        getattr(expected_job_output[exp_id], prop))
Пример #22
0
    def test_for_recently_published_explorations(self):
        """Tests for recently published explorations."""

        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, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New title'
            }], 'Changed title.')

        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])
Пример #23
0
    def _run_batch_job_once_and_verify_output(
            self,
            exp_specs,
            default_title='A title',
            default_category='A category',
            default_status=rights_manager.EXPLORATION_STATUS_PUBLICIZED):
        """Run batch job for creating exploration summaries once and
         verify its output. exp_specs is a list of dicts with
         exploration specifications. Allowed keys are category,
         status, title.  If a key is not specified, the default value
         is taken.
        """
        from core.domain import exp_services
        with self.swap(jobs_registry, 'ONE_OFF_JOB_MANAGERS',
                       self.ONE_OFF_JOB_MANAGERS_FOR_TESTS):

            # default specs
            default_specs = {
                'title': default_title,
                'category': default_category,
                'status': default_status
            }

            self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
            self.login(self.ADMIN_EMAIL)
            self.ADMIN_ID = self.get_user_id_from_email(self.ADMIN_EMAIL)
            self.set_admins([self.ADMIN_EMAIL])

            # create and delete an exploration (to make sure job handles
            # deleted explorations correctly)
            exp_id = '100'
            self.save_new_valid_exploration(exp_id,
                                            self.ADMIN_ID,
                                            title=default_specs['title'],
                                            category=default_specs['category'])
            exploration = exp_services.get_exploration_by_id(exp_id)
            exp_services.delete_exploration(self.ADMIN_ID, exp_id)

            # get dummy explorations
            num_exps = len(exp_specs)
            expected_job_output = {}

            for ind in range(num_exps):
                exp_id = str(ind)
                spec = default_specs
                spec.update(exp_specs[ind])
                self.save_new_valid_exploration(exp_id,
                                                self.ADMIN_ID,
                                                title=spec['title'],
                                                category=spec['category'])
                exploration = exp_services.get_exploration_by_id(exp_id)

                # publish or publicize exploration
                if spec['status'] == rights_manager.EXPLORATION_STATUS_PUBLIC:
                    rights_manager.publish_exploration(self.ADMIN_ID, exp_id)
                elif (spec['status'] ==
                      rights_manager.EXPLORATION_STATUS_PUBLICIZED):
                    rights_manager.publish_exploration(self.ADMIN_ID, exp_id)
                    rights_manager.publicize_exploration(self.ADMIN_ID, exp_id)

                # do not include user_id here, so all explorations are not
                # editable for now (will be updated depending on user_id
                # in galleries)
                exp_rights_model = exp_models.ExplorationRightsModel.get(
                    exp_id)

                exploration = exp_services.get_exploration_by_id(exp_id)
                exploration_model_last_updated = exploration.last_updated
                exploration_model_created_on = exploration.created_on

                # manually create the expectated summary specifying title,
                # category, etc
                expected_job_output[exp_id] = exp_domain.ExplorationSummary(
                    exp_id, spec['title'], spec['category'],
                    exploration.objective,
                    exploration.language_code, exploration.tags,
                    feconf.get_empty_ratings(), spec['status'],
                    exp_rights_model.community_owned,
                    exp_rights_model.owner_ids, exp_rights_model.editor_ids,
                    exp_rights_model.viewer_ids, exploration.version,
                    exploration_model_created_on,
                    exploration_model_last_updated)

                # calling constructor for fields that are not required
                # and have no default value does not work b/c
                # unspecified fields will be empty list in
                # expected_job_output but will be unspecified in
                # actual_job_output
                if exploration.tags:
                    expected_job_output[exp_id].tags = exploration.tags
                if exp_rights_model.owner_ids:
                    expected_job_output[exp_id].owner_ids = (
                        exp_rights_model.owner_ids)
                if exp_rights_model.editor_ids:
                    expected_job_output[exp_id].editor_ids = (
                        exp_rights_model.editor_ids)
                if exp_rights_model.viewer_ids:
                    expected_job_output[exp_id].viewer_ids = (
                        exp_rights_model.viewer_ids)
                if exploration.version:
                    expected_job_output[exp_id].version = (exploration.version)

            # run batch job
            job_id = exp_jobs.ExpSummariesCreationOneOffJob.create_new()
            exp_jobs.ExpSummariesCreationOneOffJob.enqueue(job_id)
            self.process_and_flush_pending_tasks()

            # get job output
            actual_job_output = exp_services.get_all_exploration_summaries()

            # check job output
            self.assertEqual(actual_job_output.keys(),
                             expected_job_output.keys())
            simple_props = [
                'id', 'title', 'category', 'objective', 'language_code',
                'tags', 'ratings', 'status', 'community_owned', 'owner_ids',
                'editor_ids', 'viewer_ids', 'version',
                'exploration_model_created_on',
                'exploration_model_last_updated'
            ]
            for exp_id in actual_job_output:
                for prop in simple_props:
                    self.assertEqual(
                        getattr(actual_job_output[exp_id], prop),
                        getattr(expected_job_output[exp_id], prop))
Пример #24
0
    def test_migration_job_creates_appropriate_classifier_models(self):
        """Tests that the exploration migration job creates appropriate
        classifier data models for explorations.
        """
        swap_states_schema_41 = self.swap(feconf,
                                          'CURRENT_STATE_SCHEMA_VERSION', 41)
        swap_exp_schema_46 = self.swap(exp_domain.Exploration,
                                       'CURRENT_EXP_SCHEMA_VERSION', 46)
        with swap_states_schema_41, swap_exp_schema_46:
            exp_model = exp_models.ExplorationModel(
                id=self.NEW_EXP_ID,
                category='category',
                title=self.EXP_TITLE,
                objective='Old objective',
                language_code='en',
                tags=[],
                blurb='',
                author_notes='',
                states_schema_version=41,
                init_state_name=feconf.DEFAULT_INIT_STATE_NAME,
                states={
                    'END': {
                        'classifier_model_id': None,
                        'content': {
                            'content_id': 'content',
                            'html': 'Congratulations, you have finished!',
                        },
                        'interaction': {
                            'answer_groups': [],
                            'confirmed_unclassified_answers': [],
                            'customization_args': {
                                'recommendedExplorationIds': {
                                    'value': []
                                },
                            },
                            'default_outcome': None,
                            'hints': [],
                            'id': 'EndExploration',
                            'solution': None,
                        },
                        'next_content_id_index': 0,
                        'param_changes': [],
                        'recorded_voiceovers': {
                            'voiceovers_mapping': {
                                'content': {},
                            }
                        },
                        'solicit_answer_details': False,
                        'written_translations': {
                            'translations_mapping': {
                                'content': {},
                            }
                        }
                    },
                    'Introduction': {
                        'classifier_model_id': None,
                        'content': {
                            'content_id': 'content',
                            'html': ''
                        },
                        'interaction': {
                            'answer_groups': [{
                                'outcome': {
                                    'dest': 'END',
                                    'feedback': {
                                        'content_id': 'feedback_1',
                                        'html': '<p>Correct!</p>',
                                    },
                                    'labelled_as_correct': False,
                                    'missing_prerequisite_skill_id': None,
                                    'param_changes': [],
                                    'refresher_exploration_id': None,
                                },
                                'rule_specs': [{
                                    'inputs': {
                                        'x': {
                                            'contentId': 'rule_input_3',
                                            'normalizedStrSet':
                                            ['InputString']
                                        }
                                    },
                                    'rule_type': 'Equals',
                                }],
                                'tagged_skill_misconception_id':
                                None,
                                'training_data':
                                ['answer1', 'answer2', 'answer3'],
                            }],
                            'confirmed_unclassified_answers': [],
                            'customization_args': {
                                'placeholder': {
                                    'value': {
                                        'content_id': 'ca_placeholder_2',
                                        'unicode_str': '',
                                    },
                                },
                                'rows': {
                                    'value': 1
                                },
                            },
                            'default_outcome': {
                                'dest': 'Introduction',
                                'feedback': {
                                    'content_id': 'default_outcome',
                                    'html': ''
                                },
                                'labelled_as_correct': False,
                                'missing_prerequisite_skill_id': None,
                                'param_changes': [],
                                'refresher_exploration_id': None,
                            },
                            'hints': [],
                            'id':
                            'TextInput',
                            'solution':
                            None,
                        },
                        'next_content_id_index': 4,
                        'param_changes': [],
                        'recorded_voiceovers': {
                            'voiceovers_mapping': {
                                'ca_placeholder_2': {},
                                'content': {},
                                'default_outcome': {},
                                'feedback_1': {},
                                'rule_input_3': {},
                            }
                        },
                        'solicit_answer_details': False,
                        'written_translations': {
                            'translations_mapping': {
                                'ca_placeholder_2': {},
                                'content': {},
                                'default_outcome': {},
                                'feedback_1': {},
                                'rule_input_3': {},
                            }
                        },
                    },
                },
                param_specs={},
                param_changes=[])
            rights_manager.create_new_exploration_rights(
                self.NEW_EXP_ID, self.albert_id)

        commit_message = ('New exploration created with title \'%s\'.' %
                          self.EXP_TITLE)
        exp_model.commit(self.albert_id, commit_message,
                         [{
                             'cmd': 'create_new',
                             'title': 'title',
                             'category': 'category',
                         }])
        exp_rights = exp_models.ExplorationRightsModel.get_by_id(
            self.NEW_EXP_ID)
        exp_summary_model = exp_models.ExpSummaryModel(
            id=self.NEW_EXP_ID,
            title=self.EXP_TITLE,
            category='category',
            objective='Old objective',
            language_code='en',
            tags=[],
            ratings=feconf.get_empty_ratings(),
            scaled_average_rating=feconf.EMPTY_SCALED_AVERAGE_RATING,
            status=exp_rights.status,
            community_owned=exp_rights.community_owned,
            owner_ids=exp_rights.owner_ids,
            contributor_ids=[],
            contributors_summary={})
        exp_summary_model.update_timestamps()
        exp_summary_model.put()

        exploration = exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID)

        initial_state_name = list(exploration.states.keys())[0]
        # Store classifier model for the new exploration.
        classifier_model_id = (
            classifier_models.ClassifierTrainingJobModel.create(
                'TextClassifier',
                'TextInput', self.NEW_EXP_ID, exploration.version,
                datetime.datetime.utcnow(), {}, initial_state_name,
                feconf.TRAINING_JOB_STATUS_COMPLETE, 1))
        # Store training job model for the classifier model.
        classifier_models.StateTrainingJobsMappingModel.create(
            self.NEW_EXP_ID, exploration.version, initial_state_name,
            {'TextClassifier': classifier_model_id})

        # Start migration job on sample exploration.
        job_id = exp_jobs_one_off.ExplorationMigrationJobManager.create_new()
        exp_jobs_one_off.ExplorationMigrationJobManager.enqueue(job_id)
        with self.swap(feconf, 'ENABLE_ML_CLASSIFIERS', True):
            with self.swap(feconf, 'MIN_TOTAL_TRAINING_EXAMPLES', 2):
                with self.swap(feconf, 'MIN_ASSIGNED_LABELS', 1):
                    self.process_and_flush_pending_mapreduce_tasks()

        actual_output = (
            exp_jobs_one_off.ExplorationMigrationJobManager.get_output(job_id))
        expected_output = ['[u\'SUCCESS\', 1]']
        self.assertEqual(actual_output, expected_output)

        new_exploration = exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID)
        initial_state_name = list(new_exploration.states.keys())[0]
        self.assertLess(exploration.version, new_exploration.version)
        classifier_exp_mapping_model = (
            classifier_models.StateTrainingJobsMappingModel.get_models(
                self.NEW_EXP_ID, new_exploration.version,
                [initial_state_name]))[0]
        self.assertEqual(
            classifier_exp_mapping_model.
            algorithm_ids_to_job_ids['TextClassifier'], classifier_model_id)
Пример #25
0
    def test_for_recently_published_explorations(self):
        """ Tests for recently published explorations.
        """

        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': self.get_static_asset_url(
                '/images/subjects/Lightbulb.svg'),
            'language_code': feconf.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': self.get_static_asset_url(
                '/images/subjects/Lightbulb.svg'),
            'language_code': feconf.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': self.get_static_asset_url(
                '/images/subjects/Lightbulb.svg'),
            'language_code': feconf.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, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New title'
            }], 'Changed title.')

        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])
Пример #26
0
    def save_new_exp_with_states_schema_v0(self, exp_id, user_id, title):
        """Saves a new default exploration with a default version 0 states
        dictionary.

        This function should only be used for creating explorations in tests
        involving migration of datastore explorations that use an old states
        schema version.

        Note that it makes an explicit commit to the datastore instead of using
        the usual functions for updating and creating explorations. This is
        because the latter approach would result in an exploration with the
        *current* states schema version.

        Args:
            exp_id: str. The exploration ID.
            user_id: str. The user_id of the creator.
            title: str. The title of the exploration.
        """
        exp_model = exp_models.ExplorationModel(
            id=exp_id,
            category='category',
            title=title,
            objective='Old objective',
            language_code='en',
            tags=[],
            blurb='',
            author_notes='',
            states_schema_version=0,
            init_state_name=feconf.DEFAULT_INIT_STATE_NAME,
            states=self.VERSION_0_STATES_DICT,
            param_specs={},
            param_changes=[])
        rights_manager.create_new_exploration_rights(exp_id, user_id)

        commit_message = 'New exploration created with title \'%s\'.' % title
        exp_model.commit(user_id, commit_message, [{
            'cmd': 'create_new',
            'title': 'title',
            'category': 'category',
        }])
        exp_rights = exp_models.ExplorationRightsModel.get_by_id(exp_id)
        exp_summary_model = exp_models.ExpSummaryModel(
            id=exp_id,
            title=title,
            category='category',
            objective='Old objective',
            language_code='en',
            tags=[],
            ratings=feconf.get_empty_ratings(),
            scaled_average_rating=feconf.EMPTY_SCALED_AVERAGE_RATING,
            status=exp_rights.status,
            community_owned=exp_rights.community_owned,
            owner_ids=exp_rights.owner_ids,
            contributor_ids=[],
            contributors_summary={},
        )
        exp_summary_model.put()

        # Note: Also save state id mappping model for new exploration. If not
        # saved, it may cause errors in test cases.
        exploration = exp_services.get_exploration_from_model(exp_model)
        exp_services.create_and_save_state_id_mapping_model(exploration, [])
Пример #27
0
    def _run_batch_job_once_and_verify_output(
            self, exp_specs,
            default_title='A title',
            default_category='A category',
            default_status=rights_manager.ACTIVITY_STATUS_PUBLICIZED):
        """Run batch job for creating exploration summaries once and verify its
        output. exp_specs is a list of dicts with exploration specifications.
        Allowed keys are category, status, title. If a key is not specified,
        the default value is used.
        """
        with self.swap(
            jobs_registry, 'ONE_OFF_JOB_MANAGERS',
            self.ONE_OFF_JOB_MANAGERS_FOR_TESTS
            ):

            default_spec = {
                'title': default_title,
                'category': default_category,
                'status': default_status
            }

            self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
            self.login(self.ADMIN_EMAIL)
            admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)
            self.set_admins([self.ADMIN_USERNAME])

            # Create and delete an exploration (to make sure job handles
            # deleted explorations correctly).
            exp_id = '100'
            self.save_new_valid_exploration(
                exp_id,
                admin_id,
                title=default_spec['title'],
                category=default_spec['category'])
            exploration = exp_services.get_exploration_by_id(exp_id)
            exp_services.delete_exploration(admin_id, exp_id)

            # Get dummy explorations.
            num_exps = len(exp_specs)
            expected_job_output = {}

            for ind in range(num_exps):
                exp_id = str(ind)
                spec = default_spec
                spec.update(exp_specs[ind])
                self.save_new_valid_exploration(
                    exp_id,
                    admin_id,
                    title=spec['title'],
                    category=spec['category'])
                exploration = exp_services.get_exploration_by_id(exp_id)

                # Publish or publicize exploration.
                if spec['status'] == rights_manager.ACTIVITY_STATUS_PUBLIC:
                    rights_manager.publish_exploration(admin_id, exp_id)
                elif (
                        spec['status'] ==
                        rights_manager.ACTIVITY_STATUS_PUBLICIZED):
                    rights_manager.publish_exploration(admin_id, exp_id)
                    rights_manager.publicize_exploration(admin_id, exp_id)

                # Do not include user_id here, so all explorations are not
                # editable for now (will be updated depending on user_id
                # in galleries)
                exp_rights_model = exp_models.ExplorationRightsModel.get(
                    exp_id)

                exploration = exp_services.get_exploration_by_id(exp_id)
                exploration_model_last_updated = exploration.last_updated
                exploration_model_created_on = exploration.created_on

                # Manually create the expected summary specifying title,
                # category, etc.
                expected_job_output[exp_id] = exp_domain.ExplorationSummary(
                    exp_id,
                    spec['title'],
                    spec['category'],
                    exploration.objective,
                    exploration.language_code,
                    exploration.tags,
                    feconf.get_empty_ratings(),
                    spec['status'],
                    exp_rights_model.community_owned,
                    exp_rights_model.owner_ids,
                    exp_rights_model.editor_ids,
                    exp_rights_model.viewer_ids,
                    [admin_id],
                    {admin_id: 1},
                    exploration.version,
                    exploration_model_created_on,
                    exploration_model_last_updated)

                # Note: Calling constructor for fields that are not required
                # and have no default value does not work, because
                # unspecified fields will be empty list in
                # expected_job_output but will be unspecified in
                # actual_job_output.
                if exploration.tags:
                    expected_job_output[exp_id].tags = exploration.tags
                if exp_rights_model.owner_ids:
                    expected_job_output[exp_id].owner_ids = (
                        exp_rights_model.owner_ids)
                if exp_rights_model.editor_ids:
                    expected_job_output[exp_id].editor_ids = (
                        exp_rights_model.editor_ids)
                if exp_rights_model.viewer_ids:
                    expected_job_output[exp_id].viewer_ids = (
                        exp_rights_model.viewer_ids)
                if exploration.version:
                    expected_job_output[exp_id].version = (
                        exploration.version)

            # Run batch job.
            job_id = (
                exp_jobs_one_off.ExpSummariesCreationOneOffJob.create_new())
            exp_jobs_one_off.ExpSummariesCreationOneOffJob.enqueue(job_id)
            self.process_and_flush_pending_tasks()

            # Get and check job output.
            actual_job_output = exp_services.get_all_exploration_summaries()
            self.assertEqual(
                actual_job_output.keys(), expected_job_output.keys())

            # Note: 'exploration_model_last_updated' is not expected to be the
            # same, because it is now read from the version model representing
            # the exploration's history snapshot, and not the ExplorationModel.
            simple_props = ['id', 'title', 'category', 'objective',
                            'language_code', 'tags', 'ratings', 'status',
                            'community_owned', 'owner_ids',
                            'editor_ids', 'viewer_ids',
                            'contributor_ids', 'contributors_summary',
                            'version', 'exploration_model_created_on']
            for exp_id in actual_job_output:
                for prop in simple_props:
                    self.assertEqual(
                        getattr(actual_job_output[exp_id], prop),
                        getattr(expected_job_output[exp_id], prop))