Ejemplo n.º 1
0
    def test_basic_computation_works_if_exploration_is_deleted(self):
        with self._get_test_context():
            self.save_new_valid_exploration(
                EXP_ID, USER_ID, title=EXP_TITLE, category='Category')
            last_updated_ms_before_deletion = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))
            exp_services.delete_exploration(USER_ID, EXP_ID)

            ModifiedRecentUpdatesAggregator.start_computation()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    queue_name=taskqueue_services.QUEUE_NAME_DEFAULT),
                1)
            self.process_and_flush_pending_tasks()

            recent_notifications = (
                ModifiedRecentUpdatesAggregator.get_recent_notifications(
                    USER_ID)[1])
            self.assertEqual(len(recent_notifications), 1)
            self.assertEqual(sorted(recent_notifications[0].keys()), [
                'activity_id', 'activity_title', 'author_id',
                'last_updated_ms', 'subject', 'type'])
            self.assertDictContainsSubset({
                'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': USER_ID,
                'subject': feconf.COMMIT_MESSAGE_EXPLORATION_DELETED,
            }, recent_notifications[0])
            self.assertLess(
                last_updated_ms_before_deletion,
                recent_notifications[0]['last_updated_ms'])
Ejemplo n.º 2
0
    def test_deleted_activity_is_removed_from_featured_list(self):
        rights_manager.publish_exploration(self.owner_id, self.EXP_ID_0)
        rights_manager.publish_exploration(self.owner_id, self.EXP_ID_1)
        rights_manager.publish_collection(self.owner_id, self.COL_ID_2)
        activity_services.update_featured_activity_references([
            self._create_exploration_reference(self.EXP_ID_0),
            self._create_collection_reference(self.COL_ID_2)])

        self._compare_lists(
            activity_services.get_featured_activity_references(), [
                self._create_exploration_reference(self.EXP_ID_0),
                self._create_collection_reference(self.COL_ID_2)])

        # Deleting an unfeatured activity does not affect the featured list.
        exp_services.delete_exploration(self.owner_id, self.EXP_ID_1)
        self._compare_lists(
            activity_services.get_featured_activity_references(), [
                self._create_exploration_reference(self.EXP_ID_0),
                self._create_collection_reference(self.COL_ID_2)])

        # Deleting a featured activity removes it from the featured list.
        collection_services.delete_collection(self.owner_id, self.COL_ID_2)
        self._compare_lists(
            activity_services.get_featured_activity_references(), [
                self._create_exploration_reference(self.EXP_ID_0)])
        exp_services.delete_exploration(self.owner_id, self.EXP_ID_0)
        self._compare_lists(
            activity_services.get_featured_activity_references(), [])
Ejemplo n.º 3
0
    def test_deleting_exploration_does_not_delete_subscription(self):
        exploration = exp_domain.Exploration.create_default_exploration(EXP_ID, "Title", "Category")
        exp_services.save_new_exploration(self.owner_id, exploration)
        self.assertEqual(self._get_exploration_ids_subscribed_to(self.owner_id), [EXP_ID])

        exp_services.delete_exploration(self.owner_id, EXP_ID)
        self.assertEqual(self._get_exploration_ids_subscribed_to(self.owner_id), [EXP_ID])
Ejemplo n.º 4
0
    def setUp(self):
        """Populate the database of explorations and their summaries.

        The sequence of events is:
        - (1) Albert creates EXP_ID_1.
        - (2) Bob edits the title of EXP_ID_1.
        - (3) Albert creates EXP_ID_2.
        - (4) Albert edits the title of EXP_ID_1.
        - (5) Albert edits the title of EXP_ID_2.
        - (6) Bob reverts Albert's last edit to EXP_ID_1.
        - Bob tries to publish EXP_ID_2, and is denied access.
        - (7) Albert publishes EXP_ID_2.
        - (8) Albert creates EXP_ID_3
        - (9) Albert publishes EXP_ID_3
        - (10) Albert deletes EXP_ID_3
        """
        super(ExplorationDisplayableSummaries, self).setUp()

        self.albert_id = self.get_user_id_from_email(self.ALBERT_EMAIL)
        self.bob_id = self.get_user_id_from_email(self.BOB_EMAIL)
        self.signup(self.ALBERT_EMAIL, self.ALBERT_NAME)
        self.signup(self.BOB_EMAIL, self.BOB_NAME)

        self.save_new_valid_exploration(self.EXP_ID_1, self.albert_id)

        exp_services.update_exploration(
            self.bob_id, self.EXP_ID_1, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'Exploration 1 title'
            }], 'Changed title.')

        self.save_new_valid_exploration(self.EXP_ID_2, self.albert_id)

        exp_services.update_exploration(
            self.albert_id, self.EXP_ID_1, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'Exploration 1 Albert title'
            }], 'Changed title to Albert1 title.')

        exp_services.update_exploration(
            self.albert_id, self.EXP_ID_2, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'Exploration 2 Albert title'
            }], 'Changed title to Albert2 title.')

        exp_services.revert_exploration(self.bob_id, self.EXP_ID_1, 3, 2)

        with self.assertRaisesRegexp(
            Exception, 'This exploration cannot be published'
            ):
            rights_manager.publish_exploration(self.bob_id, self.EXP_ID_2)

        rights_manager.publish_exploration(self.albert_id, self.EXP_ID_2)

        self.save_new_valid_exploration(self.EXP_ID_3, self.albert_id)
        rights_manager.publish_exploration(self.albert_id, self.EXP_ID_3)
        exp_services.delete_exploration(self.albert_id, self.EXP_ID_3)
Ejemplo n.º 5
0
    def test_migration_job_skips_deleted_explorations(self):
        """Tests that the exploration migration job skips deleted explorations
        and does not attempt to migrate.
        """
        self.save_new_exp_with_states_schema_v0(
            self.NEW_EXP_ID, self.albert_id, self.EXP_TITLE)

        # Note: This creates a summary based on the upgraded model (which is
        # fine). A summary is needed to delete the exploration.
        exp_services.create_exploration_summary(
            self.NEW_EXP_ID, None)

        # Delete the exploration before migration occurs.
        exp_services.delete_exploration(self.albert_id, self.NEW_EXP_ID)

        # Ensure the exploration is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_services.get_exploration_by_id(self.NEW_EXP_ID)

        # Start migration job on sample exploration.
        job_id = exp_jobs_one_off.ExplorationMigrationJobManager.create_new()
        exp_jobs_one_off.ExplorationMigrationJobManager.enqueue(job_id)

        # This running without errors indicates the deleted exploration is
        # being ignored, since otherwise exp_services.get_exploration_by_id
        # (used within the job) will raise an error.
        self.process_and_flush_pending_tasks()

        # Ensure the exploration is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_services.get_exploration_by_id(self.NEW_EXP_ID)
Ejemplo n.º 6
0
    def setUp(self):
        """Populate the database of explorations and their summaries.

        The sequence of events is:
        - (1) Albert creates EXP_ID_1.
        - (2) Bob edits the title of EXP_ID_1.
        - (3) Albert creates EXP_ID_2.
        - (4) Albert edits the title of EXP_ID_1.
        - (5) Albert edits the title of EXP_ID_2.
        - (6) Bob reverts Albert's last edit to EXP_ID_1.
        - Bob tries to publish EXP_ID_2, and is denied access.
        - (7) Albert publishes EXP_ID_2.
        - (8) Albert creates EXP_ID_3
        - (9) Albert publishes EXP_ID_3
        - (10) Albert deletes EXP_ID_3
        """
        super(ExplorationDisplayableSummaries, self).setUp()

        self.albert_id = self.get_user_id_from_email(self.ALBERT_EMAIL)
        self.bob_id = self.get_user_id_from_email(self.BOB_EMAIL)
        self.signup(self.ALBERT_EMAIL, self.ALBERT_NAME)
        self.signup(self.BOB_EMAIL, self.BOB_NAME)

        self.save_new_valid_exploration(self.EXP_ID_1, self.albert_id)

        exp_services.update_exploration(
            self.bob_id, self.EXP_ID_1, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'Exploration 1 title'
            }], 'Changed title.')

        self.save_new_valid_exploration(self.EXP_ID_2, self.albert_id)

        exp_services.update_exploration(
            self.albert_id, self.EXP_ID_1, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'Exploration 1 Albert title'
            }], 'Changed title to Albert1 title.')

        exp_services.update_exploration(
            self.albert_id, self.EXP_ID_2, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'Exploration 2 Albert title'
            }], 'Changed title to Albert2 title.')

        exp_services.revert_exploration(self.bob_id, self.EXP_ID_1, 3, 2)

        with self.assertRaisesRegexp(
            Exception, 'This exploration cannot be published'
            ):
            rights_manager.publish_exploration(self.bob_id, self.EXP_ID_2)

        rights_manager.publish_exploration(self.albert_id, self.EXP_ID_2)

        self.save_new_valid_exploration(self.EXP_ID_3, self.albert_id)
        rights_manager.publish_exploration(self.albert_id, self.EXP_ID_3)
        exp_services.delete_exploration(self.albert_id, self.EXP_ID_3)
Ejemplo n.º 7
0
    def test_migration_job_skips_deleted_explorations(self):
        """Tests that the exploration migration job skips deleted explorations
        and does not attempt to migrate.
        """
        self.save_new_exp_with_states_schema_v0(self.NEW_EXP_ID,
                                                self.albert_id, self.EXP_TITLE)

        # Note: This creates a summary based on the upgraded model (which is
        # fine). A summary is needed to delete the exploration.
        exp_services.create_exploration_summary(self.NEW_EXP_ID, None)

        # Delete the exploration before migration occurs.
        exp_services.delete_exploration(self.albert_id, self.NEW_EXP_ID)

        # Ensure the exploration is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_services.get_exploration_by_id(self.NEW_EXP_ID)

        # Start migration job on sample exploration.
        job_id = exp_jobs_one_off.ExplorationMigrationJobManager.create_new()
        exp_jobs_one_off.ExplorationMigrationJobManager.enqueue(job_id)

        # This running without errors indicates the deleted exploration is
        # being ignored, since otherwise exp_services.get_exploration_by_id
        # (used within the job) will raise an error.
        self.process_and_flush_pending_tasks()

        # Ensure the exploration is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_services.get_exploration_by_id(self.NEW_EXP_ID)
Ejemplo n.º 8
0
    def test_no_action_is_performed_for_deleted_exploration(self):
        """Test that no action is performed on deleted explorations."""

        exploration = exp_domain.Exploration.create_default_exploration(
            self.VALID_EXP_ID, title='title', category='category')

        exploration.add_states(['State1'])

        state1 = exploration.states['State1']

        state1.update_interaction_id('ItemSelectionInput')

        customization_args_dict = {
            'minAllowableSelectionCount': {'value': '1b'},
            'maxAllowableSelectionCount': {'value': 1},
            'choices': {'value': [{
                'html': '<p>This is value1 for ItemSelection</p>',
                'content_id': 'ca_choices_0'
            }]},
        }

        state1.update_interaction_customization_args(customization_args_dict)

        exp_services.save_new_exploration(self.albert_id, exploration)

        exp_services.delete_exploration(self.albert_id, self.VALID_EXP_ID)

        run_job_for_deleted_exp(
            self,
            interaction_jobs_one_off
            .InteractionCustomizationArgsValidationOneOffJob)
Ejemplo n.º 9
0
    def test_deleted_collection(self):
        with self.swap(
            subscription_services, 'subscribe_to_thread', self._null_fn
            ), self.swap(
                subscription_services, 'subscribe_to_exploration', self._null_fn
            ), self.swap(
                subscription_services, 'subscribe_to_collection', self._null_fn
            ):
            # User A creates and saves a new collection.
            self.save_new_default_collection(
                self.COLLECTION_ID_1, self.user_a_id)

            # User A deletes the collection.
            collection_services.delete_collection(
                self.user_a_id, self.COLLECTION_ID_1)

            # User A deletes the exploration from earlier.
            exp_services.delete_exploration(self.user_a_id, self.EXP_ID_1)

        self._run_one_off_job()

        # User A is not subscribed to the collection.
        user_a_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_a_id, strict=False)
        self.assertEqual(user_a_subscriptions_model, None)
    def test_basic_computation_works_if_exploration_is_deleted(self):
        with self._get_test_context():
            self.save_new_valid_exploration(EXP_ID,
                                            USER_ID,
                                            title=EXP_TITLE,
                                            category='Category')
            last_updated_ms_before_deletion = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))
            exp_services.delete_exploration(USER_ID, EXP_ID)

            ModifiedRecentUpdatesAggregator.start_computation()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    queue_name=taskqueue_services.QUEUE_NAME_DEFAULT), 1)
            self.process_and_flush_pending_tasks()

            recent_notifications = (ModifiedRecentUpdatesAggregator.
                                    get_recent_notifications(USER_ID)[1])
            self.assertEqual(len(recent_notifications), 1)
            self.assertEqual(sorted(recent_notifications[0].keys()), [
                'activity_id', 'activity_title', 'author_id',
                'last_updated_ms', 'subject', 'type'
            ])
            self.assertDictContainsSubset(
                {
                    'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    'activity_id': EXP_ID,
                    'activity_title': EXP_TITLE,
                    'author_id': USER_ID,
                    'subject': feconf.COMMIT_MESSAGE_EXPLORATION_DELETED,
                }, recent_notifications[0])
            self.assertLess(last_updated_ms_before_deletion,
                            recent_notifications[0]['last_updated_ms'])
Ejemplo n.º 11
0
    def test_deleted_collection(self):
        with self.swap(
                subscription_services, 'subscribe_to_thread', self._null_fn
            ), self.swap(
                subscription_services, 'subscribe_to_exploration',
                self._null_fn
            ), self.swap(
                subscription_services, 'subscribe_to_collection',
                self._null_fn):
            # User A creates and saves a new collection.
            self.save_new_default_collection(
                self.COLLECTION_ID_1, self.user_a_id)

            # User A deletes the collection.
            collection_services.delete_collection(
                self.user_a_id, self.COLLECTION_ID_1)

            # User A deletes the exploration from earlier.
            exp_services.delete_exploration(self.user_a_id, self.EXP_ID_1)

        self._run_one_off_job()

        # User A is not subscribed to the collection.
        user_a_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_a_id, strict=False)
        self.assertEqual(user_a_subscriptions_model, None)
Ejemplo n.º 12
0
    def test_no_action_is_performed_for_deleted_exploration(self):
        """Test that no action is performed on deleted explorations."""

        exploration = exp_domain.Exploration.create_default_exploration(
            self.VALID_EXP_ID, title='title', category='category')

        exploration.add_states(['State1'])

        state1 = exploration.states['State1']

        state1.update_interaction_id('ItemSelectionInput')

        customization_args_dict = {
            'choices': {'value': [{
                'html': '<p>This is value1 for ItemSelection</p>',
                'content_id': 'ca_choices_0'
            }, {
                'html': '<p>This is value2 for ItemSelection</p>',
                'content_id': 'ca_choices_1'
            }]},
            'minAllowableSelectionCount': {'value': 0},
            'maxAllowableSelectionCount': {'value': 1}
        }

        state_answer_group_list = [state_domain.AnswerGroup(
            state_domain.Outcome(
                'State1', state_domain.SubtitledHtml(
                    'feedback', '<p>Outcome for state2</p>'),
                False, [], None, None),
            [
                state_domain.RuleSpec(
                    'Equals',
                    {
                        'x': ['<p>This is value1 for ItemSelection</p>']
                    }),
                state_domain.RuleSpec(
                    'Equals',
                    {
                        'x': ['<p>This is value3 for ItemSelection</p>']
                    })
            ],
            [],
            None
        )]

        state1.update_interaction_customization_args(customization_args_dict)
        state1.update_next_content_id_index(2)
        state1.update_interaction_answer_groups(state_answer_group_list)

        exp_services.save_new_exploration(self.albert_id, exploration)

        exp_services.delete_exploration(self.albert_id, self.VALID_EXP_ID)

        run_job_for_deleted_exp(
            self,
            (
                interaction_jobs_one_off
                .RuleInputToCustomizationArgsMappingOneOffJob)
        )
Ejemplo n.º 13
0
    def test_deleting_exploration_does_not_delete_subscription(self):
        exploration = exp_domain.Exploration.create_default_exploration(EXP_ID)
        exp_services.save_new_exploration(self.owner_id, exploration)
        self.assertEqual(
            self._get_exploration_ids_subscribed_to(self.owner_id), [EXP_ID])

        exp_services.delete_exploration(self.owner_id, EXP_ID)
        self.assertEqual(
            self._get_exploration_ids_subscribed_to(self.owner_id), [EXP_ID])
    def test_no_action_is_performed_for_deleted_exploration(self):
        """Test that no action is performed on deleted explorations."""

        exploration = exp_domain.Exploration.create_default_exploration(
            self.VALID_EXP_ID, title='title', category='category')

        exploration.add_states(['State1'])

        state1 = exploration.states['State1']

        state1.update_interaction_id('DragAndDropSortInput')

        customization_args_dict = {
            'choices': {'value': [{
                'html': '<p>This is value1 for DragAndDropSortInput</p>',
                'content_id': 'ca_choices_0'
            }, {
                'html': '<p>This is value2 for DragAndDropSortInput</p>',
                'content_id': 'ca_choices_1'
            }]},
            'allowMultipleItemsInSamePosition': {'value': True}
        }

        answer_group_list = [{
            'rule_specs': [{
                'rule_type': 'IsEqualToOrdering',
                'inputs': {'x': []}
            }, {
                'rule_type': 'IsEqualToOrdering',
                'inputs': {'x': []}
            }],
            'outcome': {
                'dest': 'State1',
                'feedback': {
                    'content_id': 'feedback',
                    'html': '<p>Outcome for state2</p>'
                },
                'param_changes': [],
                'labelled_as_correct': False,
                'refresher_exploration_id': None,
                'missing_prerequisite_skill_id': None
            },
            'training_data': [],
            'tagged_skill_misconception_id': None
        }]

        state1.update_interaction_customization_args(customization_args_dict)
        state1.update_next_content_id_index(2)
        state1.update_interaction_answer_groups(answer_group_list)

        exp_services.save_new_exploration(self.albert_id, exploration)

        exp_services.delete_exploration(self.albert_id, self.VALID_EXP_ID)

        run_job_for_deleted_exp(
            self,
            interaction_jobs_one_off.DragAndDropSortInputInteractionOneOffJob)
Ejemplo n.º 15
0
    def test_deleted_exploration(self):
        self.save_new_valid_exploration(self.EXP_ID, self.user_a_id)
        exp_services.delete_exploration(feconf.SYSTEM_COMMITTER_ID,
                                        self.EXP_ID)

        self.process_and_flush_pending_tasks()

        output = self._run_one_off_job()
        self.assertEqual([], output)
Ejemplo n.º 16
0
    def test_one_answer_ignored_for_deleted_exploration(self):
        with self.swap(stats_jobs_continuous,
                       'InteractionAnswerSummariesAggregator',
                       MockInteractionAnswerSummariesAggregator):

            # Setup example exploration.
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            first_state_name = exp.init_state_name
            exp_services.update_exploration('*****@*****.**', exp_id, [
                exp_domain.ExplorationChange(
                    {
                        'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                        'state_name': first_state_name,
                        'property_name':
                        exp_domain.STATE_PROPERTY_INTERACTION_ID,
                        'new_value': 'MultipleChoiceInput',
                    })
            ], 'Update interaction type')
            exp = exp_fetchers.get_exploration_by_id(exp_id)
            exp_version = exp.version

            time_spent = 5.0
            params = {}

            self._record_start(exp_id, exp_version, first_state_name,
                               'session1')
            self.process_and_flush_pending_tasks()

            # Add an answer.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, exp_version, first_state_name, 'MultipleChoiceInput',
                0, 0, exp_domain.EXPLICIT_CLASSIFICATION, 'session1',
                time_spent, params, 'answer1')

            # Delete the exploration.
            exp_services.delete_exploration('*****@*****.**', exp_id)

            # Now run the job.
            (stats_jobs_continuous.InteractionAnswerSummariesAggregator.
             start_computation())
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    taskqueue_services.QUEUE_NAME_CONTINUOUS_JOBS), 1)
            self.process_and_flush_pending_tasks()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    taskqueue_services.QUEUE_NAME_CONTINUOUS_JOBS), 0)

            # There should be no job output corresponding to all versions since
            # the exploration was deleted before the job could run. Note that
            # this applies regardless of whether the job runs before or after
            # deletion of the exploration.
            calc_output_model = self._get_calc_output_model(
                exp_id, first_state_name, 'AnswerFrequencies')
            self.assertIsNone(calc_output_model)
Ejemplo n.º 17
0
    def test_no_action_is_performed_for_deleted_exploration(self):
        """Test that no action is performed on deleted explorations."""

        exploration = exp_domain.Exploration.create_default_exploration(
            self.VALID_EXP_ID, title='title', category='category')

        exploration.add_states(['State1'])

        state1 = exploration.states['State1']

        state1.update_interaction_id('DragAndDropSortInput')

        customization_args_dict = {
            'choices': {'value': [{
                'html': '<p>This is value1 for DragAndDropSortInput</p>',
                'content_id': 'ca_choices_0'
            }, {
                'html': '<p>This is value2 for DragAndDropSortInput</p>',
                'content_id': 'ca_choices_1'
            }]},
            'allowMultipleItemsInSamePosition': {'value': True}
        }

        state_answer_groups = [state_domain.AnswerGroup(
            state_domain.Outcome(
                'State1', state_domain.SubtitledHtml(
                    'feedback', '<p>Outcome for state2</p>'),
                False, [], None, None),
            [
                state_domain.RuleSpec(
                    'IsEqualToOrdering',
                    {
                        'x': []
                    }),
                state_domain.RuleSpec(
                    'IsEqualToOrdering',
                    {
                        'x': []
                    })
            ],
            [],
            None
        )]

        state1.update_interaction_customization_args(customization_args_dict)
        state1.update_next_content_id_index(2)
        state1.update_interaction_answer_groups(state_answer_groups)

        exp_services.save_new_exploration(self.albert_id, exploration)

        exp_services.delete_exploration(self.albert_id, self.VALID_EXP_ID)

        run_job_for_deleted_exp(
            self,
            interaction_jobs_one_off.DragAndDropSortInputInteractionOneOffJob)
Ejemplo n.º 18
0
    def test_deleting_activity_does_not_delete_subscription(self):
        EXP_ID = 'exp_id'
        exploration = exp_domain.Exploration.create_default_exploration(
            EXP_ID, 'Title', 'Category')
        exp_services.save_new_exploration(self.owner_id, exploration)
        self.assertEqual(self._get_activity_ids_subscribed_to(self.owner_id),
                         [EXP_ID])

        exp_services.delete_exploration(self.owner_id, EXP_ID)
        self.assertEqual(self._get_activity_ids_subscribed_to(self.owner_id),
                         [EXP_ID])
Ejemplo n.º 19
0
    def test_migration_job_skips_deleted_explorations(self):
        """Tests that the exploration migration job skips deleted explorations
        and does not attempt to migrate.
        """
        # Save new default exploration with a default version 0 states
        # dictionary.
        exp_model = exp_models.ExplorationModel(
            id=self.NEW_EXP_ID,
            category='category',
            title='title',
            objective='',
            language_code='en',
            tags=[],
            blurb='',
            author_notes='',
            default_skin='conversation_v1',
            skin_customizations={'panels_contents': {}},
            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(
            self.NEW_EXP_ID, self.ALBERT_ID)
        exp_model.commit(self.ALBERT_ID, 'old commit', [{
            'cmd': 'create_new',
            'title': 'title',
            'category': 'category',
        }])

        # Note: This creates a summary based on the upgraded model (which is
        # fine). A summary is needed to delete the exploration.
        exp_services.create_exploration_summary(self.NEW_EXP_ID)

        # Delete the exploration before migration occurs.
        exp_services.delete_exploration(self.ALBERT_ID, self.NEW_EXP_ID)

        # Ensure the exploration is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_services.get_exploration_by_id(self.NEW_EXP_ID)

        # Start migration job on sample exploration.
        job_id = exp_jobs.ExplorationMigrationJobManager.create_new()
        exp_jobs.ExplorationMigrationJobManager.enqueue(job_id)

        # This running without errors indicates the deleted exploration is being
        # ignored, since otherwise exp_services.get_exploration_by_id (used
        # within the job) will raise an error.
        self.process_and_flush_pending_tasks()

        # Ensure the exploration is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_services.get_exploration_by_id(self.NEW_EXP_ID)
Ejemplo n.º 20
0
    def map(item):
        """Deletes the exploration with the given id if it is of the 'Copies'
        category.

        Args:
            item: ExplorationModel. An exploration storage model.
        """
        if item.category == 'Copies':
            exp_services.delete_exploration(feconf.SYSTEM_COMMITTER_ID,
                                            item.id,
                                            force_deletion=True)
Ejemplo n.º 21
0
    def test_deleting_activity_does_not_delete_subscription(self):
        EXP_ID = 'exp_id'
        exploration = exp_domain.Exploration.create_default_exploration(
            EXP_ID, 'Title', 'Category')
        exp_services.save_new_exploration(self.owner_id, exploration)
        self.assertEqual(
            self._get_activity_ids_subscribed_to(self.owner_id), [EXP_ID])

        exp_services.delete_exploration(self.owner_id, EXP_ID)
        self.assertEqual(
            self._get_activity_ids_subscribed_to(self.owner_id), [EXP_ID])
Ejemplo n.º 22
0
    def test_deleting_exploration_does_not_delete_subscription(self) -> None:
        exploration = exp_domain.Exploration.create_default_exploration(
            EXP_ID)  # type: ignore[no-untyped-call]
        exp_services.save_new_exploration(
            self.owner_id, exploration)  # type: ignore[no-untyped-call]
        self.assertEqual(
            self._get_exploration_ids_subscribed_to(self.owner_id), [EXP_ID])

        exp_services.delete_exploration(
            self.owner_id, EXP_ID)  # type: ignore[no-untyped-call]
        self.assertEqual(
            self._get_exploration_ids_subscribed_to(self.owner_id), [EXP_ID])
Ejemplo n.º 23
0
 def test_create_two_explorations_delete_one_and_visit_dashboard(self):
     self.login(self.OWNER_EMAIL_1)
     self.save_new_default_exploration(
         self.EXP_ID_1, self.owner_id_1, title=self.EXP_TITLE_1)
     self.save_new_default_exploration(
         self.EXP_ID_2, self.owner_id_1, title=self.EXP_TITLE_2)
     # Testing the quantity of exploration and it should be 2.
     response = self.get_json(feconf.CREATOR_DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 2)
     exp_services.delete_exploration(self.owner_id_1, self.EXP_ID_1)
     # Testing whether 1 exploration left after deletion of previous one.
     response = self.get_json(feconf.CREATOR_DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 1)
     self.logout()
Ejemplo n.º 24
0
 def map(model):
     exp_model = exp_models.ExplorationModel.get(model.exploration_id,
                                                 strict=False)
     if exp_model is None:
         # By using delete_exploration we combine a few things, we delete
         # the ExplorationUserDataModels, also we delete any other models
         # that still exist for the exploration ID, and finally we verify
         # that the delete_exploration works correctly.
         exp_services.delete_exploration(feconf.SYSTEM_COMMITTER_ID,
                                         model.exploration_id,
                                         force_deletion=True)
         yield ('SUCCESS_DELETED_EXPLORATION', 1)
     else:
         yield ('SUCCESS_KEPT', 1)
Ejemplo n.º 25
0
    def test_get_learner_dict_with_deleted_exp_fails_validation(self):
        self.save_new_valid_collection(
            self.COLLECTION_ID, self.owner_id, exploration_id=self.EXP_ID)
        summary_services.get_learner_collection_dict_by_id(
            self.COLLECTION_ID, self.owner_id)

        exp_services.delete_exploration(self.owner_id, self.EXP_ID)

        with self.assertRaisesRegexp(
            utils.ValidationError,
            'Expected collection to only reference valid explorations, but '
            'found an exploration with ID: exploration_id'):
            summary_services.get_learner_collection_dict_by_id(
                self.COLLECTION_ID, self.owner_id)
Ejemplo n.º 26
0
    def test_get_learner_dict_with_deleted_exp_fails_validation(self):
        self.save_new_valid_collection(
            self.COLLECTION_ID, self.owner_id, exploration_id=self.EXP_ID)
        summary_services.get_learner_collection_dict_by_id(
            self.COLLECTION_ID, self.owner)

        exp_services.delete_exploration(self.owner_id, self.EXP_ID)

        with self.assertRaisesRegex(
            utils.ValidationError,
            'Expected collection to only reference valid explorations, but '
            'found an exploration with ID: exploration_id'):
            summary_services.get_learner_collection_dict_by_id(
                self.COLLECTION_ID, self.owner)
Ejemplo n.º 27
0
 def test_create_two_explorations_delete_one_and_visit_dashboard(self):
     self.login(self.OWNER_EMAIL_1)
     self.save_new_default_exploration(
         self.EXP_ID_1, self.owner_id_1, title=self.EXP_TITLE_1)
     self.save_new_default_exploration(
         self.EXP_ID_2, self.owner_id_1, title=self.EXP_TITLE_2)
     # Testing the quantity of exploration and it should be 2.
     response = self.get_json(feconf.DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 2)
     exp_services.delete_exploration(self.owner_id_1, self.EXP_ID_1)
     # Testing whether 1 exploration left after deletion of previous one.
     response = self.get_json(feconf.DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 1)
     self.logout()
Ejemplo n.º 28
0
    def test_deleted_exploration(self):
        with self.swap(
                subscription_services, 'subscribe_to_thread', self._null_fn
            ), self.swap(
                subscription_services, 'subscribe_to_activity', self._null_fn):

            # User A deletes the exploration.
            exp_services.delete_exploration(self.user_a_id, self.EXP_ID)

        self._run_one_off_job()

        # User A is not subscribed to the exploration.
        user_a_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_a_id, strict=False)
        self.assertEqual(user_a_subscriptions_model, None)
Ejemplo n.º 29
0
    def delete(self, exploration_id):
        """Deletes the given exploration."""

        log_debug_string = '(%s) %s tried to delete exploration %s' % (
            self.role, self.user_id, exploration_id)
        logging.debug(log_debug_string)

        is_exploration_cloned = rights_manager.is_exploration_cloned(
            exploration_id)
        exp_services.delete_exploration(
            self.user_id, exploration_id, force_deletion=is_exploration_cloned)

        log_info_string = '(%s) %s deleted exploration %s' % (
            self.role, self.user_id, exploration_id)
        logging.info(log_info_string)
Ejemplo n.º 30
0
    def test_run_cron_to_hard_delete_versioned_models_marked_as_deleted(self):
        self.login(self.ADMIN_EMAIL, is_super_admin=True)
        admin_user_id = self.get_user_id_from_email(self.ADMIN_EMAIL)

        with self.mock_datetime_utcnow(datetime.datetime.utcnow() -
                                       self.NINE_WEEKS):
            self.save_new_default_exploration('exp_id', admin_user_id)
            exp_services.delete_exploration(admin_user_id, 'exp_id')

        self.assertIsNotNone(exp_models.ExplorationModel.get_by_id('exp_id'))

        with self.testapp_swap:
            self.get_html_response('/cron/models/cleanup')

        self.assertIsNone(exp_models.ExplorationModel.get_by_id('exp_id'))
Ejemplo n.º 31
0
    def test_deleted_exploration(self):
        with self.swap(subscription_services, 'subscribe_to_thread',
                       self._null_fn), self.swap(subscription_services,
                                                 'subscribe_to_exploration',
                                                 self._null_fn):

            # User A deletes the exploration.
            exp_services.delete_exploration(self.user_a_id, self.EXP_ID_1)

        self._run_one_off_job()

        # User A is not subscribed to the exploration.
        user_a_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_a_id, strict=False)
        self.assertEqual(user_a_subscriptions_model, None)
Ejemplo n.º 32
0
    def test_migration_job_skips_deleted_explorations(self):
        """Tests that the exploration migration job skips deleted explorations
        and does not attempt to migrate.
        """
        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:
            exploration = exp_domain.Exploration.create_default_exploration(
                self.NEW_EXP_ID, title=self.EXP_TITLE)
            exp_services.save_new_exploration(self.albert_id, exploration)

        # Note: This creates a summary based on the upgraded model (which is
        # fine). A summary is needed to delete the exploration.
        exp_services.regenerate_exploration_and_contributors_summaries(
            self.NEW_EXP_ID)

        # Delete the exploration before migration occurs.
        exp_services.delete_exploration(self.albert_id, self.NEW_EXP_ID)

        # Ensure the exploration is deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID)

        # Start migration job on sample exploration.
        job_id = exp_jobs_one_off.ExpSnapshotsMigrationJob.create_new()
        exp_jobs_one_off.ExpSnapshotsMigrationJob.enqueue(job_id)

        # This running without errors indicates the deleted exploration is
        # being ignored, since otherwise exp_fetchers.get_exploration_by_id
        # (used within the job) will raise an error.
        self.process_and_flush_pending_mapreduce_tasks()

        actual_output = (
            exp_jobs_one_off.ExpSnapshotsMigrationJob.get_output(job_id))
        expected_output_choices = [
            '[u\'INFO - Exploration does not exist\', [u\'%s-1\', u\'%s-2\']]'
            % (self.NEW_EXP_ID, self.NEW_EXP_ID),
            '[u\'INFO - Exploration does not exist\', [u\'%s-2\', u\'%s-1\']]'
            % (self.NEW_EXP_ID, self.NEW_EXP_ID)
        ]
        self.assertEqual(len(actual_output), 1)
        self.assertIn(actual_output[0], expected_output_choices)

        # Ensure the exploration is still deleted.
        with self.assertRaisesRegexp(Exception, 'Entity .* not found'):
            exp_fetchers.get_exploration_by_id(self.NEW_EXP_ID)
Ejemplo n.º 33
0
    def test_deleted_exps_are_not_returned(self):
        exp_services.delete_exploration(self.albert_id, self.EXP_ID2)

        metadata_dicts = (summary_services.get_exploration_metadata_dicts(
            [self.EXP_ID2, self.EXP_ID3, self.EXP_ID4], self.bob_id))

        expected_metadata_dicts = [{
            'id': self.EXP_ID3,
            'objective': u'An objective 3',
            'title': u'Exploration 3 Albert title',
        }, {
            'id': self.EXP_ID4,
            'objective': u'An objective 4',
            'title': u'Exploration 4 Bob title',
        }]
        self.assertEqual(expected_metadata_dicts, metadata_dicts)
Ejemplo n.º 34
0
    def test_deleted_exps_are_not_returned(self):
        exp_services.delete_exploration(self.albert_id, self.EXP_ID2)

        metadata_dicts = (summary_services.get_exploration_metadata_dicts(
            [self.EXP_ID2, self.EXP_ID3, self.EXP_ID4], self.bob))

        expected_metadata_dicts = [{
            'id': self.EXP_ID3,
            'objective': u'An objective 3',
            'title': u'Exploration 3 Albert title',
        }, {
            'id': self.EXP_ID4,
            'objective': u'An objective 4',
            'title': u'Exploration 4 Bob title',
        }]
        self.assertEqual(expected_metadata_dicts, metadata_dicts)
Ejemplo n.º 35
0
    def delete(self, exploration_id):
        """Deletes the given exploration."""
        role = self.request.get('role')
        if not role:
            role = None

        if role == rights_manager.ROLE_ADMIN:
            if not self.is_admin:
                logging.error(
                    '%s tried to delete an exploration, but is not an admin.'
                    % self.user_id)
                raise self.UnauthorizedUserException(
                    'User %s does not have permissions to delete exploration '
                    '%s' % (self.user_id, exploration_id))
        elif role == rights_manager.ROLE_MODERATOR:
            if not self.is_moderator:
                logging.error(
                    '%s tried to delete an exploration, but is not a '
                    'moderator.' % self.user_id)
                raise self.UnauthorizedUserException(
                    'User %s does not have permissions to delete exploration '
                    '%s' % (self.user_id, exploration_id))
        elif role is not None:
            raise self.InvalidInputException('Invalid role: %s' % role)

        logging.info(
            '%s %s tried to delete exploration %s' %
            (role, self.user_id, exploration_id))

        exploration = exp_services.get_exploration_by_id(exploration_id)
        can_delete = rights_manager.Actor(self.user_id).can_delete(
            rights_manager.ACTIVITY_TYPE_EXPLORATION, exploration.id)
        if not can_delete:
            raise self.UnauthorizedUserException(
                'User %s does not have permissions to delete exploration %s' %
                (self.user_id, exploration_id))

        is_exploration_cloned = rights_manager.is_exploration_cloned(
            exploration_id)
        exp_services.delete_exploration(
            self.user_id, exploration_id, force_deletion=is_exploration_cloned)

        logging.info(
            '%s %s deleted exploration %s' %
            (role, self.user_id, exploration_id))
Ejemplo n.º 36
0
    def test_exploration_deletion_is_handled(self):
        exp_services.delete_exploration(feconf.SYSTEM_COMMITTER_ID,
                                        self.exp_id)

        job_id = stats_jobs_one_off.GenerateV1StatisticsJob.create_new()
        stats_jobs_one_off.GenerateV1StatisticsJob.enqueue(job_id)

        self.assertEqual(
            self.count_jobs_in_taskqueue(
                taskqueue_services.QUEUE_NAME_ONE_OFF_JOBS), 1)
        self.process_and_flush_pending_tasks()

        exploration_stats = stats_services.get_exploration_stats_by_id(
            self.exp_id, self.exploration.version)

        # Since exploration is deleted, ExplorationStatsModel instance is not
        # created.
        self.assertEqual(exploration_stats, None)
Ejemplo n.º 37
0
def pre_delete_user(user_id):
    """Prepare user for the full deletion.
        1. Mark all the activities that are private and solely owned by the user
           being deleted as deleted.
        2. Disable all the email preferences.
        3. Mark the user as to be deleted.
        4. Create PendingDeletionRequestModel for the user.

    Args:
        user_id: str. The id of the user to be deleted.
    """
    subscribed_exploration_summaries = (
        exp_fetchers.get_exploration_summaries_subscribed_to(user_id))
    explorations_to_be_deleted_ids = [
        exp_summary.id for exp_summary in subscribed_exploration_summaries
        if exp_summary.is_private()
        and exp_summary.is_solely_owned_by_user(user_id)
    ]
    # TODO(#8301): Implement delete_explorations to make this efficient.
    for exp_id in explorations_to_be_deleted_ids:
        exp_services.delete_exploration(user_id, exp_id)

    subscribed_collection_summaries = (
        collection_services.get_collection_summaries_subscribed_to(user_id))
    collections_to_be_deleted_ids = [
        col_summary.id for col_summary in subscribed_collection_summaries
        if col_summary.is_private()
        and col_summary.is_solely_owned_by_user(user_id)
    ]
    # TODO(#8301): Implement delete_collections to make this efficient.
    for col_id in collections_to_be_deleted_ids:
        collection_services.delete_collection(user_id, col_id)

    # Set all the user's email preferences to False in order to disable all
    # ordinary emails that could be sent to the users.
    user_services.update_email_preferences(user_id, False, False, False, False)

    user_services.mark_user_for_deletion(
        user_id,
        explorations_to_be_deleted_ids,
        collections_to_be_deleted_ids,
    )
Ejemplo n.º 38
0
    def test_hard_deletion_of_explorations(self):
        """Test that hard deletion of explorations works correctly."""
        self.save_new_default_exploration(self.EXP_ID)

        exp_services.delete_exploration(
            self.OWNER_ID, self.EXP_ID, force_deletion=True)
        with self.assertRaises(Exception):
            exp_services.get_exploration_by_id(self.EXP_ID)

        # The deleted exploration does not show up in any queries.
        self.assertEqual(
            exp_services.get_owned_explorations_summary_dict(self.OWNER_ID),
            {})

        # The exploration model has been purged from the backend.
        self.assertNotIn(
            self.EXP_ID,
            [exp.id for exp in exp_models.ExplorationModel.get_all(
                include_deleted_entities=True)]
        )
Ejemplo n.º 39
0
    def test_soft_deletion_of_explorations(self):
        """Test that soft deletion of explorations works correctly."""
        # TODO(sll): Add tests for deletion of states and version snapshots.

        self.save_new_default_exploration(self.EXP_ID)

        exp_services.delete_exploration(self.OWNER_ID, self.EXP_ID)
        with self.assertRaises(Exception):
            exp_services.get_exploration_by_id(self.EXP_ID)

        # The deleted exploration does not show up in any queries.
        self.assertEqual(
            exp_services.get_owned_explorations_summary_dict(self.OWNER_ID),
            {})

        # But the models still exist in the backend.
        self.assertIn(
            self.EXP_ID,
            [exp.id for exp in exp_models.ExplorationModel.get_all(
                include_deleted_entities=True)]
        )
Ejemplo n.º 40
0
    def test_root_redirect_rules_for_logged_in_creators(self):
        self.login(self.TEST_CREATOR_EMAIL)
        creator_user_id = self.get_user_id_from_email(self.TEST_CREATOR_EMAIL)
        exploration_id = '0_en_test_exploration'
        self.save_new_valid_exploration(
            exploration_id, creator_user_id, title='Test',
            category='Test', language_code='en')

        # Since at least one exploration has been created, going to '/' should
        # redirect to the dashboard page.
        response = self.testapp.get('/')
        self.assertIn('dashboard', response.headers['location'])
        exp_services.delete_exploration(creator_user_id, exploration_id)
        self.logout()
        self.login(self.TEST_CREATOR_EMAIL)
        response = self.testapp.get('/')

        # Even though the exploration is deleted, the user is still redirected
        # to the dashboard. This is because deleted explorations are still
        # associated with their creators.
        self.assertEqual(response.status_int, 302)
        self.assertIn('dashboard', response.headers['location'])
Ejemplo n.º 41
0
    def delete(self, exploration_id):
        """Deletes the given exploration."""

        role_description = ''
        if self.is_admin:
            role_description = 'admin'
        elif self.is_moderator:
            role_description = 'moderator'

        log_debug_string = ''
        if role_description == '':
            log_debug_string = '%s tried to delete exploration %s' % (
                self.user_id, exploration_id)
        else:
            log_debug_string = '(%s) %s tried to delete exploration %s' % (
                role_description, self.user_id, exploration_id)
        logging.debug(log_debug_string)

        exploration = exp_services.get_exploration_by_id(exploration_id)
        can_delete = rights_manager.Actor(self.user_id).can_delete(
            feconf.ACTIVITY_TYPE_EXPLORATION, exploration.id)
        if not can_delete:
            raise self.UnauthorizedUserException(
                'User %s does not have permissions to delete exploration %s' %
                (self.user_id, exploration_id))

        is_exploration_cloned = rights_manager.is_exploration_cloned(
            exploration_id)
        exp_services.delete_exploration(
            self.user_id, exploration_id, force_deletion=is_exploration_cloned)

        log_info_string = ''
        if role_description == '':
            log_info_string = '%s deleted exploration %s' % (
                self.user_id, exploration_id)
        else:
            log_info_string = '(%s) %s deleted exploration %s' % (
                role_description, self.user_id, exploration_id)
        logging.info(log_info_string)
Ejemplo n.º 42
0
    def delete(self, exploration_id):
        """Deletes the given exploration."""

        role_description = ''
        if self.is_admin:
            role_description = 'admin'
        elif self.is_moderator:
            role_description = 'moderator'

        log_debug_string = ''
        if role_description == '':
            log_debug_string = '%s tried to delete exploration %s' % (
                self.user_id, exploration_id)
        else:
            log_debug_string = '(%s) %s tried to delete exploration %s' % (
                role_description, self.user_id, exploration_id)
        logging.debug(log_debug_string)

        exploration = exp_services.get_exploration_by_id(exploration_id)
        can_delete = rights_manager.Actor(self.user_id).can_delete(
            feconf.ACTIVITY_TYPE_EXPLORATION, exploration.id)
        if not can_delete:
            raise self.UnauthorizedUserException(
                'User %s does not have permissions to delete exploration %s' %
                (self.user_id, exploration_id))

        is_exploration_cloned = rights_manager.is_exploration_cloned(
            exploration_id)
        exp_services.delete_exploration(
            self.user_id, exploration_id, force_deletion=is_exploration_cloned)

        log_info_string = ''
        if role_description == '':
            log_info_string = '%s deleted exploration %s' % (
                self.user_id, exploration_id)
        else:
            log_info_string = '(%s) %s deleted exploration %s' % (
                role_description, self.user_id, exploration_id)
        logging.info(log_info_string)
Ejemplo n.º 43
0
    def test_basic_computation_works_if_exploration_is_deleted(self):
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CONTINUOUS_COMPUTATION_MANAGERS_FOR_TESTS):
            EXP_ID = 'eid'
            EXP_TITLE = 'Title'
            USER_ID = 'user_id'

            self.save_new_valid_exploration(EXP_ID,
                                            USER_ID,
                                            title=EXP_TITLE,
                                            category='Category')
            last_updated_ms_before_deletion = utils.get_time_in_millisecs(
                exp_services.get_exploration_by_id(EXP_ID).last_updated)
            exp_services.delete_exploration(USER_ID, EXP_ID)

            ModifiedRecentUpdatesAggregator.start_computation()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    queue_name=taskqueue_services.QUEUE_NAME_DEFAULT), 1)
            self.process_and_flush_pending_tasks()

            recent_notifications = (ModifiedRecentUpdatesAggregator.
                                    get_recent_notifications(USER_ID)[1])
            self.assertEqual(len(recent_notifications), 1)
            self.assertEqual(sorted(recent_notifications[0].keys()), [
                'activity_id', 'activity_title', 'author_id',
                'last_updated_ms', 'subject', 'type'
            ])
            self.assertDictContainsSubset(
                {
                    'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    'activity_id': EXP_ID,
                    'activity_title': EXP_TITLE,
                    'author_id': USER_ID,
                    'subject': feconf.COMMIT_MESSAGE_EXPLORATION_DELETED,
                }, recent_notifications[0])
            self.assertLess(last_updated_ms_before_deletion,
                            recent_notifications[0]['last_updated_ms'])
Ejemplo n.º 44
0
    def test_basic_computation_works_if_exploration_is_deleted(self):
        with self.swap(
                jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                self.ALL_CONTINUOUS_COMPUTATION_MANAGERS_FOR_TESTS):
            EXP_ID = 'eid'
            EXP_TITLE = 'Title'
            USER_ID = 'user_id'

            self.save_new_valid_exploration(
                EXP_ID, USER_ID, title=EXP_TITLE, category='Category')
            last_updated_ms_before_deletion = utils.get_time_in_millisecs(
                exp_services.get_exploration_by_id(EXP_ID).last_updated)
            exp_services.delete_exploration(USER_ID, EXP_ID)

            ModifiedRecentUpdatesAggregator.start_computation()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    queue_name=taskqueue_services.QUEUE_NAME_DEFAULT),
                1)
            self.process_and_flush_pending_tasks()

            recent_notifications = (
                ModifiedRecentUpdatesAggregator.get_recent_notifications(
                    USER_ID)[1])
            self.assertEqual(len(recent_notifications), 1)
            self.assertEqual(sorted(recent_notifications[0].keys()), [
                'activity_id', 'activity_title', 'author_id',
                'last_updated_ms', 'subject', 'type'])
            self.assertDictContainsSubset({
                'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': USER_ID,
                'subject': feconf.COMMIT_MESSAGE_EXPLORATION_DELETED,
            }, recent_notifications[0])
            self.assertLess(
                last_updated_ms_before_deletion,
                recent_notifications[0]['last_updated_ms'])
Ejemplo n.º 45
0
    def test_root_redirect_rules_for_logged_in_creators(self):
        self.login(self.TEST_CREATOR_EMAIL)
        creator_user_id = self.get_user_id_from_email(self.TEST_CREATOR_EMAIL)
        exploration_id = '0_en_test_exploration'
        self.save_new_valid_exploration(exploration_id,
                                        creator_user_id,
                                        title='Test',
                                        category='Test',
                                        language_code='en')

        # Since at least one exploration has been created, going to '/' should
        # redirect to the dashboard page.
        response = self.testapp.get('/')
        self.assertIn('dashboard', response.headers['location'])
        exp_services.delete_exploration(creator_user_id, exploration_id)
        self.logout()
        self.login(self.TEST_CREATOR_EMAIL)
        response = self.testapp.get('/')

        # Even though the exploration is deleted, the user is still redirected
        # to the dashboard. This is because deleted explorations are still
        # associated with their creators.
        self.assertEqual(response.status_int, 302)
        self.assertIn('dashboard', response.headers['location'])
Ejemplo n.º 46
0
 def test_create_multiple_explorations_delete_all_and_visit_dashboard(self):
     self.login(self.OWNER_EMAIL_2)
     self.save_new_default_exploration(
         self.EXP_ID_1, self.owner_id_2, title=self.EXP_TITLE_1)
     self.save_new_default_exploration(
         self.EXP_ID_2, self.owner_id_2, title=self.EXP_TITLE_2)
     self.save_new_default_exploration(
         self.EXP_ID_3, self.owner_id_2, title=self.EXP_TITLE_3)
     # Testing for quantity of explorations to be 3.
     response = self.get_json(feconf.CREATOR_DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 3)
     # Testing for deletion of all created previously.
     exp_services.delete_exploration(self.owner_id_2, self.EXP_ID_1)
     exp_services.delete_exploration(self.owner_id_2, self.EXP_ID_2)
     exp_services.delete_exploration(self.owner_id_2, self.EXP_ID_3)
     # All explorations have been deleted, so the dashboard query should not
     # load any explorations.
     response = self.get_json(feconf.CREATOR_DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 0)
     self.logout()
Ejemplo n.º 47
0
 def test_create_multiple_explorations_delete_all_and_visit_dashboard(self):
     self.login(self.OWNER_EMAIL_2)
     self.save_new_default_exploration(
         self.EXP_ID_1, self.owner_id_2, title=self.EXP_TITLE_1)
     self.save_new_default_exploration(
         self.EXP_ID_2, self.owner_id_2, title=self.EXP_TITLE_2)
     self.save_new_default_exploration(
         self.EXP_ID_3, self.owner_id_2, title=self.EXP_TITLE_3)
     # Testing for quantity of explorations to be 3.
     response = self.get_json(feconf.DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 3)
     # Testing for deletion of all created previously.
     exp_services.delete_exploration(self.owner_id_2, self.EXP_ID_1)
     exp_services.delete_exploration(self.owner_id_2, self.EXP_ID_2)
     exp_services.delete_exploration(self.owner_id_2, self.EXP_ID_3)
     # All explorations have been deleted, so the dashboard query should not
     # load any explorations.
     response = self.get_json(feconf.DASHBOARD_DATA_URL)
     self.assertEqual(len(response['explorations_list']), 0)
     self.logout()
Ejemplo n.º 48
0
    def map(item):
        from core.domain import exp_services

        if item.category == "Copies":
            exp_services.delete_exploration(feconf.SYSTEM_COMMITTER_ID, item.id, force_deletion=True)
Ejemplo n.º 49
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.skill_tags,
                    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.skill_tags:
                    expected_job_output[exp_id].skill_tags = (
                        exploration.skill_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', 'skill_tags', '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))
Ejemplo n.º 50
0
    def test_gallery_handler_for_created_explorations(self):
        """Test the gallery data handler for manually created explirations."""
        self.set_admins([self.ADMIN_USERNAME])

        self.login(self.ADMIN_EMAIL)
        response_dict = self.get_json(feconf.GALLERY_DATA_URL)
        self.assertEqual({
            'is_admin': True,
            'is_moderator': True,
            'is_super_admin': False,
            'explorations_list': [],
            'user_email': self.ADMIN_EMAIL,
            'username': self.ADMIN_USERNAME,
            'search_cursor': None,
            'profile_picture_data_url': None,
            'preferred_language_codes': [feconf.DEFAULT_LANGUAGE_CODE],
        }, response_dict)

        # Create exploration A
        exploration = self.save_new_valid_exploration(
            'A', self.admin_id, title='Title A', category='Category A',
            objective='Objective A')
        exp_services._save_exploration(  # pylint: disable=protected-access
            self.admin_id, exploration, 'Exploration A', [])

        # Test that the private exploration isn't displayed.
        response_dict = self.get_json(feconf.GALLERY_DATA_URL)
        self.assertEqual(response_dict['explorations_list'], [])

        # Create exploration B
        exploration = self.save_new_valid_exploration(
            'B', self.admin_id, title='Title B', category='Category B',
            objective='Objective B')
        exp_services._save_exploration(  # pylint: disable=protected-access
            self.admin_id, exploration, 'Exploration B', [])
        rights_manager.publish_exploration(self.admin_id, 'B')
        rights_manager.publicize_exploration(self.admin_id, 'B')

        # Publish exploration A
        rights_manager.publish_exploration(self.admin_id, 'A')

        exp_services.index_explorations_given_ids(['A', 'B'])

        # Test gallery
        response_dict = self.get_json(feconf.GALLERY_DATA_URL)
        self.assertEqual(len(response_dict['explorations_list']), 2)
        self.assertDictContainsSubset({
            'id': 'B',
            'category': 'Category B',
            'title': 'Title B',
            'language_code': 'en',
            'objective': 'Objective B',
            'status': rights_manager.ACTIVITY_STATUS_PUBLICIZED,
        }, response_dict['explorations_list'][0])
        self.assertDictContainsSubset({
            'id': 'A',
            'category': 'Category A',
            'title': 'Title A',
            'language_code': 'en',
            'objective': 'Objective A',
            'status': rights_manager.ACTIVITY_STATUS_PUBLIC,
        }, response_dict['explorations_list'][1])

        # Delete exploration A
        exp_services.delete_exploration(self.admin_id, 'A')

        # Test gallery
        response_dict = self.get_json(feconf.GALLERY_DATA_URL)
        self.assertEqual(len(response_dict['explorations_list']), 1)
        self.assertDictContainsSubset({
            'id': 'B',
            'category': 'Category B',
            'title': 'Title B',
            'language_code': 'en',
            'objective': 'Objective B',
            'status': rights_manager.ACTIVITY_STATUS_PUBLICIZED,
        }, response_dict['explorations_list'][0])
Ejemplo n.º 51
0
 def delete(self, exploration_id):
     """Deletes the given exploration."""
     exp_services.delete_exploration(self.user_id, exploration_id)
Ejemplo n.º 52
0
 def map(item):
     if item.category == 'Copies':
         exp_services.delete_exploration(
             feconf.SYSTEM_COMMITTER_ID, item.id, force_deletion=True)
Ejemplo n.º 53
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))