Exemple #1
0
    def setUp(self):
        """Create exploration with two versions"""
        super(VersioningIntegrationTest, self).setUp()

        self.EXP_ID = "0"

        exp_services.load_demo(self.EXP_ID)
        rights_manager.release_ownership_of_exploration(feconf.SYSTEM_COMMITTER_ID, self.EXP_ID)

        self.login(self.EDITOR_EMAIL)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

        # In version 2, change the objective and the initial state content.
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(
            self.editor_id,
            self.EXP_ID,
            [
                {"cmd": "edit_exploration_property", "property_name": "objective", "new_value": "the objective"},
                {
                    "cmd": "edit_state_property",
                    "property_name": "content",
                    "state_name": exploration.init_state_name,
                    "new_value": [{"type": "text", "value": "ABC"}],
                },
            ],
            "Change objective and init state content",
        )
Exemple #2
0
    def test_update_widget_handlers(self):
        """Test updating of widget_handlers."""

        # We create a second state to use as a rule destination
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exploration.add_states(['State 2'])
        exp_services._save_exploration(self.OWNER_ID, exploration, '', [])

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        self.widget_handlers['submit'][1]['dest'] = 'State 2'
        exp_services.update_exploration(
            self.OWNER_ID, self.EXP_ID,
            _get_change_list(
                self.init_state_name, 'widget_id', 'MultipleChoiceInput') +
            _get_change_list(
                self.init_state_name, 'widget_handlers', self.widget_handlers),
            '')

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        rule_specs = exploration.init_state.widget.handlers[0].rule_specs
        self.assertEqual(rule_specs[0].definition, {
            'rule_type': 'atomic',
            'name': 'Equals',
            'inputs': {'x': 0},
            'subject': 'answer'
        })
        self.assertEqual(rule_specs[0].feedback, ['Try again'])
        self.assertEqual(rule_specs[0].dest, self.init_state_name)
        self.assertEqual(rule_specs[1].dest, 'State 2')
Exemple #3
0
    def map(item):
        if item.deleted:
            return

        # Do not upgrade explorations that fail non-strict validation.
        old_exploration = exp_services.get_exploration_by_id(item.id)
        try:
            old_exploration.validate()
        except Exception as e:
            logging.error(
                'Exploration %s failed non-strict validation: %s' %
                (item.id, e))
            return

        # If the exploration model being stored in the datastore is not the
        # most up-to-date states schema version, then update it.
        if (item.states_schema_version !=
                feconf.CURRENT_EXPLORATION_STATES_SCHEMA_VERSION):
            # Note: update_exploration does not need to apply a change list in
            # order to perform a migration. See the related comment in
            # exp_services.apply_change_list for more information.
            commit_cmds = [{
                'cmd': exp_domain.CMD_MIGRATE_STATES_SCHEMA_TO_LATEST_VERSION,
                'from_version': str(item.states_schema_version),
                'to_version': str(
                    feconf.CURRENT_EXPLORATION_STATES_SCHEMA_VERSION)
            }]
            exp_services.update_exploration(
                feconf.MIGRATION_BOT_USERNAME, item.id, commit_cmds,
                'Update exploration states from schema version %d to %d.' % (
                    item.states_schema_version,
                    feconf.CURRENT_EXPLORATION_STATES_SCHEMA_VERSION))
    def test_community_owned_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 adds user B as an editor to the exploration.
            rights_manager.assign_role_for_exploration(
                self.user_a_id, self.EXP_ID_1, self.user_b_id,
                rights_manager.ROLE_EDITOR)
            # The exploration becomes community-owned.
            rights_manager.publish_exploration(self.user_a_id, self.EXP_ID_1)
            rights_manager.release_ownership_of_exploration(
                self.user_a_id, self.EXP_ID_1)
            # User C edits the exploration.
            exp_services.update_exploration(
                self.user_c_id, self.EXP_ID_1, [], 'Update exploration')

        self._run_one_off_job()

        # User A and user B are subscribed to the exploration; user C is not.
        user_a_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_a_id)
        user_b_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_b_id)
        user_c_subscriptions_model = user_models.UserSubscriptionsModel.get(
            self.user_c_id, strict=False)

        self.assertEqual(
            user_a_subscriptions_model.activity_ids, [self.EXP_ID_1])
        self.assertEqual(
            user_b_subscriptions_model.activity_ids, [self.EXP_ID_1])
        self.assertEqual(user_c_subscriptions_model, None)
    def test_that_last_edited_time_is_updated(self):
        self.login(self.OWNER_EMAIL)
        self.save_new_valid_exploration(
            self.exp_id, self.owner_id, end_state_name='End')
        self.logout()
        self.login(self.EDITOR_EMAIL)
        exp_services.update_exploration(self.editor_id, self.exp_id, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')
        self.logout()

        user_settings = user_services.get_user_settings(self.editor_id)
        user_settings.last_edited_an_exploration = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        editor_settings = user_services.get_user_settings(self.editor_id)

        self.assertIsNone(editor_settings.last_created_an_exploration)
        self.assertIsNone(editor_settings.last_edited_an_exploration)

        self._run_one_off_job()

        editor_settings = user_services.get_user_settings(self.editor_id)

        self.assertIsNotNone(editor_settings.last_edited_an_exploration)
        self.assertIsNone(editor_settings.last_created_an_exploration)
    def test_contribution_msec_updates_on_published_explorations(self):
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.admin_id, end_state_name='End')
        init_state_name = exploration.init_state_name

        # Test that no contribution time is set.
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)

        # Test all owners and editors of exploration after publication have
        # updated times.
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)
        rights_manager.release_ownership_of_exploration(
            self.admin_id, self.EXP_ID)
        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
                'cmd': 'edit_state_property',
                'state_name': init_state_name,
                'property_name': 'widget_id',
                'new_value': 'MultipleChoiceInput'
            }], 'commit')
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)
        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
    def setUp(self):
        super(UserContributionsOneOffJobTests, self).setUp()
        # User A has no created or edited explorations
        # User B has one created exploration
        # User C has one edited exploration
        # User D has created an exploration and then edited it.
        # (This is used to check that there are no duplicate
        # entries in the contribution lists.)
        self.signup(self.USER_A_EMAIL, self.USER_A_USERNAME)
        self.user_a_id = self.get_user_id_from_email(self.USER_A_EMAIL)
        self.signup(self.USER_B_EMAIL, self.USER_B_USERNAME)
        self.user_b_id = self.get_user_id_from_email(self.USER_B_EMAIL)
        self.signup(self.USER_C_EMAIL, self.USER_C_USERNAME)
        self.user_c_id = self.get_user_id_from_email(self.USER_C_EMAIL)
        self.signup(self.USER_D_EMAIL, self.USER_D_USERNAME)
        self.user_d_id = self.get_user_id_from_email(self.USER_D_EMAIL)

        self.save_new_valid_exploration(
            self.EXP_ID_1, self.user_b_id, end_state_name='End')

        exp_services.update_exploration(self.user_c_id, self.EXP_ID_1, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        self.save_new_valid_exploration(
            self.EXP_ID_2, self.user_d_id, end_state_name='End')

        exp_services.update_exploration(self.user_d_id, self.EXP_ID_2, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')
Exemple #8
0
    def test_basic_computation_with_an_update_after_creation(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'
            ANOTHER_USER_ID = 'another_user_id'

            self.save_new_valid_exploration(
                EXP_ID, USER_ID, title=EXP_TITLE, category='Category')
            # Another user makes a commit; this, too, shows up in the
            # original user's dashboard.
            exp_services.update_exploration(
                ANOTHER_USER_ID, EXP_ID, [], 'Update exploration')
            expected_last_updated_ms = utils.get_time_in_millisecs(
                exp_services.get_exploration_by_id(EXP_ID).last_updated)

            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_updates = (
                ModifiedRecentUpdatesAggregator.get_recent_updates(USER_ID)[1])
            self.assertEqual([{
                'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                'last_updated_ms': expected_last_updated_ms,
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': ANOTHER_USER_ID,
                'subject': 'Update exploration',
            }], recent_updates)
    def test_exploration_multiple_contributors(self):
        # Sign up a user and have them create an exploration.
        user_a_id = self._sign_up_user(
            self.USER_A_EMAIL, self.USER_A_USERNAME)
        user_b_id = self._sign_up_user(
            self.USER_B_EMAIL, self.USER_B_USERNAME)
        exploration = self._create_exploration(self.EXP_ID_1, user_a_id)
        # Give this exploration an average rating of 4
        avg_rating = 4
        self._rate_exploration(exploration.id, 5, avg_rating)
        exp_services.update_exploration(user_b_id, self.EXP_ID_1, [], '')

        # See state counts in _mock_get_statistics(), above.
        expected_answer_count = 8
        reach = expected_answer_count ** self.EXPONENT
        contrib = 0.5
        expected_user_impact_score = round(
            ((avg_rating - 2) * reach * contrib) ** self.EXPONENT)

        # Verify that the impact score matches the expected.
        self._run_computation()
        user_stats_model = user_models.UserStatsModel.get(user_a_id)
        self.assertEqual(
            user_stats_model.impact_score, expected_user_impact_score)
        user_stats_model = user_models.UserStatsModel.get(user_b_id)
        self.assertEqual(
            user_stats_model.impact_score, expected_user_impact_score)
Exemple #10
0
    def test_root_redirect_rules_for_logged_in_editors(self):
        self.login(self.TEST_CREATOR_EMAIL)
        creator_user_id = self.get_user_id_from_email(self.TEST_CREATOR_EMAIL)
        editor_user_id = self.get_user_id_from_email(self.TEST_EDITOR_EMAIL)
        exploration_id = '1_en_test_exploration'
        self.save_new_valid_exploration(
            exploration_id, creator_user_id, title='Test',
            category='Test', language_code='en')
        rights_manager.assign_role_for_exploration(
            creator_user_id, exploration_id, editor_user_id,
            rights_manager.ROLE_EDITOR)
        self.logout()
        self.login(self.TEST_EDITOR_EMAIL)
        exp_services.update_exploration(
            editor_user_id, exploration_id, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'edited title'
            }, {
                'cmd': 'edit_exploration_property',
                'property_name': 'category',
                'new_value': 'edited category'
            }], 'Change title and category')

        # Since user has edited one exploration created by another user,
        # going to '/' should redirect to the dashboard page.
        response = self.testapp.get('/')
        self.assertEqual(response.status_int, 302)
        self.assertIn('dashboard', response.headers['location'])
        self.logout()
    def setUp(self):
        super(UserQueryJobOneOffTests, self).setUp()
        # User A has no created or edited explorations
        # User B has one created exploration
        # User C has one edited exploration
        # User D has created an exploration and then edited it.
        # User E has created an exploration 10 days before.
        # Submitter is the user who submits the query.
        self.signup(self.USER_A_EMAIL, self.USER_A_USERNAME)
        self.user_a_id = self.get_user_id_from_email(self.USER_A_EMAIL)
        self.signup(self.USER_B_EMAIL, self.USER_B_USERNAME)
        self.user_b_id = self.get_user_id_from_email(self.USER_B_EMAIL)
        self.signup(self.USER_C_EMAIL, self.USER_C_USERNAME)
        self.user_c_id = self.get_user_id_from_email(self.USER_C_EMAIL)
        self.signup(self.USER_D_EMAIL, self.USER_D_USERNAME)
        self.user_d_id = self.get_user_id_from_email(self.USER_D_EMAIL)
        self.signup(self.USER_E_EMAIL, self.USER_E_USERNAME)
        self.user_e_id = self.get_user_id_from_email(self.USER_E_EMAIL)
        self.signup(self.USER_SUBMITTER_EMAIL, self.USER_SUBMITTER_USERNAME)
        self.submitter_id = self.get_user_id_from_email(
            self.USER_SUBMITTER_EMAIL)

        self.save_new_valid_exploration(
            self.EXP_ID_1, self.user_b_id, end_state_name='End')

        exp_services.update_exploration(self.user_c_id, self.EXP_ID_1, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        self.save_new_valid_exploration(
            self.EXP_ID_2, self.user_d_id, end_state_name='End')

        exp_services.update_exploration(self.user_d_id, self.EXP_ID_2, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        self.save_new_valid_exploration(
            self.EXP_ID_3, self.user_e_id, end_state_name='End')
        user_e_settings = user_services.get_user_settings(self.user_e_id)
        user_e_settings.last_created_an_exploration = (
            user_e_settings.last_created_an_exploration -
            datetime.timedelta(days=10))
        # Last edited time also changes when user creates an explorationan.
        user_e_settings.last_edited_an_exploration = (
            datetime.datetime.utcnow() - datetime.timedelta(days=10))
        user_e_settings.last_logged_in = (
            user_e_settings.last_logged_in - datetime.timedelta(days=10))
        user_services._save_user_settings(user_e_settings) # pylint: disable=protected-access

        user_a_settings = user_services.get_user_settings(self.user_a_id)
        user_a_settings.last_logged_in = (
            user_a_settings.last_logged_in - datetime.timedelta(days=3))
        user_services._save_user_settings(user_a_settings) # pylint: disable=protected-access

        # Set tmpsuperadm1n as admin in ADMIN_USERNAMES config property.
        self.set_admins(['tmpsuperadm1n'])
Exemple #12
0
    def setUp(self):
        """Create exploration with two versions"""
        super(VersioningIntegrationTest, self).setUp()

        self.EXP_ID = '0'

        exp_services.delete_demo(self.EXP_ID)
        exp_services.load_demo(self.EXP_ID)

        EDITOR_EMAIL = '*****@*****.**'
        self.register_editor(EDITOR_EMAIL)
        self.login(EDITOR_EMAIL)

        # In version 2, change the objective and the initial state content.
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(
           EDITOR_EMAIL, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'the objective',
            }, {
                'cmd': 'edit_state_property',
                'property_name': 'content',
                'state_name': exploration.init_state_name,
                'new_value': [{'type': 'text', 'value': 'ABC'}],
            }], 'Change objective and init state content')
Exemple #13
0
    def setUp(self):
        """Create exploration with two versions"""
        super(VersioningIntegrationTest, self).setUp()

        self.EXP_ID = '0'

        exp_services.load_demo(self.EXP_ID)
        rights_manager.release_ownership_of_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID)

        self.login(self.EDITOR_EMAIL)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

        # In version 2, change the objective and the initial state content.
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'the objective',
            }, {
                'cmd': 'edit_state_property',
                'property_name': 'content',
                'state_name': exploration.init_state_name,
                'new_value': [{'type': 'text', 'value': 'ABC'}],
            }], 'Change objective and init state content')
    def test_basic_computation_with_an_update_after_exploration_is_created(
            self):
        with self._get_test_context():
            self.save_new_valid_exploration(
                EXP_ID, USER_ID, title=EXP_TITLE, category='Category')
            # Another user makes a commit; this, too, shows up in the
            # original user's dashboard.
            exp_services.update_exploration(
                ANOTHER_USER_ID, EXP_ID, [], 'Update exploration')
            expected_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(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([{
                'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                'last_updated_ms': expected_last_updated_ms,
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': ANOTHER_USER_ID,
                'subject': 'Update exploration',
            }], recent_notifications)
Exemple #15
0
    def test_edited(self):
        # Check that the profile page for a user who has created
        # a single exploration shows 0 created and 1 edited exploration.
        self.signup(self.EMAIL_A, self.USERNAME_A)
        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

        self.signup(self.EMAIL_B, self.USERNAME_B)
        user_b_id = self.get_user_id_from_email(self.EMAIL_B)

        self.save_new_valid_exploration(
            self.EXP_ID_1, user_a_id, end_state_name='End')
        rights_manager.publish_exploration(user_a_id, self.EXP_ID_1)

        exp_services.update_exploration(user_b_id, self.EXP_ID_1, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        response_dict = self.get_json(
            '/profilehandler/data/%s' % self.USERNAME_B)
        self.assertEqual(len(
            response_dict['created_exp_summary_dicts']), 0)
        self.assertEqual(len(
            response_dict['edited_exp_summary_dicts']), 1)
        self.assertEqual(
            response_dict['edited_exp_summary_dicts'][0]['id'],
            self.EXP_ID_1)
        self.assertEqual(
            response_dict['edited_exp_summary_dicts'][0]['objective'],
            'the objective')
Exemple #16
0
    def setUp(self):
        """Create exploration with two versions"""
        super(VersioningIntegrationTest, self).setUp()

        self.EXP_ID = "0"

        exp_services.load_demo(self.EXP_ID)

        self.login(self.EDITOR_EMAIL)

        # In version 2, change the objective and the initial state content.
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(
            self.EDITOR_EMAIL,
            self.EXP_ID,
            [
                {"cmd": "edit_exploration_property", "property_name": "objective", "new_value": "the objective"},
                {
                    "cmd": "edit_state_property",
                    "property_name": "content",
                    "state_name": exploration.init_state_name,
                    "new_value": [{"type": "text", "value": "ABC"}],
                },
            ],
            "Change objective and init state content",
        )
    def test_last_exp_edit_time_gets_updated(self):
        exp_services.update_exploration(self.editor_id, self.EXP_ID, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'the objective'
        }], 'Test edit')

        # Decrease last exploration edited time by 13 hours.
        user_settings = user_services.get_user_settings(self.editor_id)
        user_settings.last_edited_an_exploration = (
            user_settings.last_edited_an_exploration -
            datetime.timedelta(hours=13))
        user_services._save_user_settings(user_settings) # pylint: disable=protected-access

        editor_settings = user_services.get_user_settings(self.editor_id)
        previous_last_edited_an_exploration = (
            editor_settings.last_edited_an_exploration)
        self.assertIsNotNone(previous_last_edited_an_exploration)

        # The editor edits the exploration 13 hours after it was created.
        exp_services.update_exploration(self.editor_id, self.EXP_ID, [{
            'cmd': 'edit_exploration_property',
            'property_name': 'objective',
            'new_value': 'new objective'
        }], 'Test edit 2')

        # Make sure last exploration edited time gets updated.
        editor_settings = user_services.get_user_settings(self.editor_id)
        self.assertGreater(
            (editor_settings.last_edited_an_exploration),
            previous_last_edited_an_exploration)
Exemple #18
0
    def test_contribution_msec_updates_on_published_explorations(self):
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, self.admin_id, end_state_name='End')
        init_state_name = exploration.init_state_name
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)

        # Test all owners and editors of exploration after publication have
        # updated first contribution times in msecs.
        self.assertIsNotNone(user_services.get_user_settings(
            self.admin_id).first_contribution_msec)

        # Test editor of published exploration has updated contribution time.
        rights_manager.release_ownership_of_exploration(
            self.admin_id, self.EXP_ID)

        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
                'cmd': 'edit_state_property',
                'state_name': init_state_name,
                'property_name': 'widget_id',
                'new_value': 'MultipleChoiceInput'
            }], 'commit')

        self.assertIsNotNone(user_services.get_user_settings(
            self.editor_id).first_contribution_msec)
    def test_nonhuman_committers_not_counted(self):
        """Test that only human committers are counted as contributors.
        """
        # Create a commit with the system user id.
        exploration = self.save_new_valid_exploration(
            self.EXP_ID, feconf.SYSTEM_COMMITTER_ID, title='Original Title')

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

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

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

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        for system_id in feconf.SYSTEM_USER_IDS:
            self.assertNotIn(
                system_id,
                exploration_summary.contributors_summary)
Exemple #20
0
 def test_update_widget_sticky_type(self):
     """Test for error if widget_sticky is made non-Boolean."""
     with self.assertRaisesRegexp(
             utils.ValidationError,
             'Expected widget sticky flag to be a boolean, received 3'):
         exp_services.update_exploration(
             self.OWNER_ID, self.EXP_ID, _get_change_list(
                 self.init_state_name, 'widget_sticky', 3), '')
Exemple #21
0
 def test_update_invalid_param_changes(self):
     """Check that updates cannot be made to non-existent parameters."""
     with self.assertRaisesRegexp(
             utils.ValidationError,
             r'The parameter myParam .* does not exist .*'):
         exp_services.update_exploration(
             self.OWNER_ID, self.EXP_ID, _get_change_list(
                 self.init_state_name, 'param_changes', self.param_changes),
             '')
Exemple #22
0
    def test_update_widget_id(self):
        """Test updating of widget_id."""
        exp_services.update_exploration(
            self.OWNER_ID, self.EXP_ID, _get_change_list(
                self.init_state_name, 'widget_id', 'MultipleChoiceInput'), '')

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        self.assertEqual(
            exploration.init_state.widget.widget_id, 'MultipleChoiceInput')
Exemple #23
0
    def test_demand_commit_message(self):
        """Check published explorations demand commit messages"""
        rights_manager.publish_exploration(self.OWNER_ID, self.EXP_ID)

        with self.assertRaisesRegexp(
                ValueError, 'Exploration is public so expected a commit '
                            'message but received none.'):
            exp_services.update_exploration(
                self.OWNER_ID, self.EXP_ID, _get_change_list(
                    self.init_state_name, 'widget_sticky', False), '')
Exemple #24
0
 def test_update_content_missing_key(self):
     """Test that missing keys in content yield an error."""
     with self.assertRaisesRegexp(KeyError, 'type'):
         exp_services.update_exploration(
             self.OWNER_ID, self.EXP_ID, _get_change_list(
                 self.init_state_name, 'content', [{
                     'value': '<b>Test content</b>',
                     '$$hashKey': '014'
                 }]),
             '')
Exemple #25
0
 def test_update_state_missing_keys(self):
     """Test that missing keys in widget_handlers produce an error."""
     del self.widget_handlers['submit'][0]['definition']['inputs']
     with self.assertRaisesRegexp(KeyError, 'inputs'):
         exp_services.update_exploration(
             self.OWNER_ID, self.EXP_ID,
             _get_change_list(
                 self.init_state_name, 'widget_id', 'NumericInput') +
             _get_change_list(
                 self.init_state_name, 'widget_handlers',
                 self.widget_handlers),
             '')
Exemple #26
0
 def _create_and_update_fake_exploration(self, exp_id):
     self.save_new_valid_exploration(exp_id, '*****@*****.**')
     exp_services.update_exploration('*****@*****.**', exp_id, [{
         'cmd': 'add_state',
         'state_name': self.STATE_2_NAME,
     }, {
         'cmd': 'edit_state_property',
         'state_name': self.STATE_2_NAME,
         'property_name': 'widget_id',
         'new_value': 'TextInput',
     }], 'Add new state')
     return exp_services.get_exploration_by_id(exp_id)
Exemple #27
0
    def test_update_state_name_with_unicode(self):
        """Test updating of state name to one that uses unicode characters."""
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(self.OWNER_ID, self.EXP_ID, [{
            'cmd': 'rename_state',
            'old_state_name': '(untitled state)',
            'new_state_name': u'¡Hola! αβγ',
        }], 'Change state name')

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        self.assertIn(u'¡Hola! αβγ', exploration.states)
        self.assertNotIn('(untitled state)', exploration.states)
Exemple #28
0
    def test_update_state_name(self):
        """Test updating of state name."""
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(self.OWNER_ID, self.EXP_ID, [{
            'cmd': 'rename_state',
            'old_state_name': '(untitled state)',
            'new_state_name': 'new name',
        }], 'Change state name')

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        self.assertIn('new name', exploration.states)
        self.assertNotIn('(untitled state)', exploration.states)
Exemple #29
0
    def test_record_commit_message(self):
        """Check published explorations record commit messages."""
        rights_manager.publish_exploration(self.OWNER_ID, self.EXP_ID)

        exp_services.update_exploration(
            self.OWNER_ID, self.EXP_ID, _get_change_list(
                self.init_state_name, 'widget_sticky', False), 'A message')

        self.assertEqual(
            exp_services.get_exploration_snapshots_metadata(
                self.EXP_ID, 1)[0]['commit_message'],
            'A message')
Exemple #30
0
 def test_update_state_extra_keys(self):
     """Test that extra keys in rule definitions are detected."""
     self.widget_handlers['submit'][0]['definition']['extra'] = 3
     with self.assertRaisesRegexp(
             utils.ValidationError, 'should conform to schema'):
         exp_services.update_exploration(
             self.OWNER_ID, self.EXP_ID,
             _get_change_list(
                 self.init_state_name, 'widget_id', 'MultipleChoiceInput') +
             _get_change_list(
                 self.init_state_name, 'widget_handlers',
                 self.widget_handlers),
             '')
Exemple #31
0
    def test_error_on_no_training_job_for_retrieving_model(self):
        new_exp_id = 'new_exp'
        new_exp = self.save_new_default_exploration(new_exp_id,
                                                    feconf.SYSTEM_COMMITTER_ID,
                                                    title='New title')

        change_list = [
            exp_domain.ExplorationChange({
                'cmd': 'edit_state_property',
                'state_name': new_exp.init_state_name,
                'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                'new_value': 'NumericInput'
            }),
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name':
                exp_domain.STATE_PROPERTY_INTERACTION_CUST_ARGS,
                'state_name': new_exp.init_state_name,
                'new_value': {
                    'requireNonnegativeInput': {
                        'value': False
                    }
                }
            })
        ]

        with self.swap(feconf, 'ENABLE_ML_CLASSIFIERS', True):
            exp_services.update_exploration(feconf.SYSTEM_COMMITTER_ID,
                                            new_exp_id, change_list, '')

        params = {
            'exploration_id': new_exp_id,
            'exploration_version': new_exp.version,
            'state_name': new_exp.init_state_name,
        }

        self.get_json('/ml/trainedclassifierhandler',
                      params=params,
                      expected_status_int=404)
Exemple #32
0
    def put(self, exploration_id):
        """Updates properties of the given exploration."""
        exploration = exp_services.get_exploration_by_id(exploration_id)
        version = self.payload.get('version')
        _require_valid_version(version, exploration.version)

        commit_message = self.payload.get('commit_message')
        change_list_dict = self.payload.get('change_list')
        change_list = [
            exp_domain.ExplorationChange(change) for change in change_list_dict
        ]
        try:
            exp_services.update_exploration(self.user_id, exploration_id,
                                            change_list, commit_message)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        exploration_data = exp_services.get_user_exploration_data(
            self.user_id, exploration_id)

        self.values.update(exploration_data)
        self.render_json(self.values)
Exemple #33
0
    def map(item):
        if item.deleted:
            return

        # Do not upgrade explorations that fail non-strict validation.
        old_exploration = exp_services.get_exploration_by_id(item.id)
        try:
            old_exploration.validate()
        except Exception as e:
            logging.error('Exploration %s failed non-strict validation: %s' %
                          (item.id, e))
            return

        # If the exploration model being stored in the datastore is not the
        # most up-to-date states schema version, then update it.
        if (item.states_schema_version !=
                feconf.CURRENT_STATES_SCHEMA_VERSION):
            # Note: update_exploration does not need to apply a change list in
            # order to perform a migration. See the related comment in
            # exp_services.apply_change_list for more information.
            #
            # Note: from_version and to_version really should be int, but left
            # as str to conform with legacy data.
            commit_cmds = [
                exp_domain.ExplorationChange({
                    'cmd':
                    exp_domain.CMD_MIGRATE_STATES_SCHEMA_TO_LATEST_VERSION,
                    'from_version':
                    str(item.states_schema_version),
                    'to_version':
                    str(feconf.CURRENT_STATES_SCHEMA_VERSION)
                })
            ]
            exp_services.update_exploration(
                feconf.MIGRATION_BOT_USERNAME, item.id, commit_cmds,
                'Update exploration states from schema version %d to %d.' %
                (item.states_schema_version,
                 feconf.CURRENT_STATES_SCHEMA_VERSION))
            yield ('SUCCESS', item.id)
Exemple #34
0
    def test_legacy_user(self):
        """Test the case of a user who are editing exploration for first time
        after the last edited time check was introduced.
        """
        # Set up a 'previous-generation' user.
        user_settings = user_services.get_user_settings(self.editor_id)
        user_settings.last_edited_an_exploration = None
        user_services._save_user_settings(user_settings)  # pylint: disable=protected-access

        editor_settings = user_services.get_user_settings(self.editor_id)
        self.assertIsNone(editor_settings.last_edited_an_exploration)

        exp_services.update_exploration(self.editor_id, self.EXP_ID, [
            exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'the objective'
            })
        ], 'Test edit')

        editor_settings = user_services.get_user_settings(self.editor_id)
        self.assertIsNotNone(editor_settings.last_edited_an_exploration)
Exemple #35
0
    def test_contributors_for_valid_nonrevert_contribution(self):
        """Test that if only non-revert commits are made by
        contributor then the contributions summary shows same
        exact number of commits for that contributor's ID.
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

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

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

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

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

        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)

        self.assertEqual(3,
                         exploration_summary.contributors_summary[user_a_id])
    def test_exploration_multiple_contributors(self):
        exploration = self._create_exploration(self.EXP_ID_1, self.user_a_id)
        # Give this exploration an average rating of 4
        avg_rating = 4
        self._rate_exploration(exploration.id, 5, avg_rating)
        exp_services.update_exploration(self.user_b_id, self.EXP_ID_1, [], '')

        # See state counts in _mock_get_statistics(), above.
        expected_answer_count = 8
        reach = expected_answer_count**self.EXPONENT
        contrib = 0.5
        expected_user_impact_score = round(
            ((avg_rating - 2) * reach * contrib)**self.EXPONENT)

        # Verify that the impact score matches the expected.
        self._run_computation()
        user_stats_model = user_models.UserStatsModel.get(self.user_a_id)
        self.assertEqual(user_stats_model.impact_score,
                         expected_user_impact_score)
        user_stats_model = user_models.UserStatsModel.get(self.user_b_id)
        self.assertEqual(user_stats_model.impact_score,
                         expected_user_impact_score)
    def test_contribution_msec_updates_on_published_explorations(self):
        exploration = self.save_new_valid_exploration(self.EXP_ID,
                                                      self.admin_id,
                                                      end_state_name='End')
        init_state_name = exploration.init_state_name

        # Test that no contribution time is set.
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)

        # Test all owners and editors of exploration after publication have
        # updated times.
        exp_services.publish_exploration_and_update_user_profiles(
            self.admin_id, self.EXP_ID)
        rights_manager.release_ownership_of_exploration(
            self.admin_id, self.EXP_ID)
        exp_services.update_exploration(self.editor_id, self.EXP_ID,
                                        [{
                                            'cmd': 'edit_state_property',
                                            'state_name': init_state_name,
                                            'property_name': 'widget_id',
                                            'new_value': 'MultipleChoiceInput'
                                        }], 'commit')
        job_id = (
            user_jobs_one_off.UserFirstContributionMsecOneOffJob.create_new())
        user_jobs_one_off.UserFirstContributionMsecOneOffJob.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.admin_id).first_contribution_msec)
        self.assertIsNotNone(
            user_services.get_user_settings(
                self.editor_id).first_contribution_msec)
    def test_reverts_not_counted(self):
        """Test that if both non-revert commits and revert are
        made by contributor then the contributions summary shows
        only non-revert commits for that contributor. However,
        the commits made after the version to which we have reverted
        shouldn't be counted either.
        """

        user_a_id = self.get_user_id_from_email(self.EMAIL_A)

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

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

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

        # Check that USER A's number of contributions is equal to 2
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual(2, exploration_summary.contributors_summary[user_a_id])
Exemple #39
0
    def test_version_number_errors_for_get_multiple_exploration_versions(
            self) -> None:
        # Update exploration to version 2.
        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_ADD_STATE,
                'state_name': 'New state',
            })
        ]
        exp_services.update_exploration(  # type: ignore[no-untyped-call]
            feconf.SYSTEM_COMMITTER_ID, self.EXP_1_ID, change_list, '')

        # Update exploration to version 3.
        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_ADD_STATE,
                'state_name': 'New state 2',
            })
        ]
        exp_services.update_exploration(  # type: ignore[no-untyped-call]
            feconf.SYSTEM_COMMITTER_ID, self.EXP_1_ID, change_list, '')

        with self.assertRaisesRegex(  # type: ignore[no-untyped-call]
                ValueError,
                'Requested version number 4 cannot be higher than the current '
                'version number 3.'):
            (exp_fetchers.
             get_multiple_versioned_exp_interaction_ids_mapping_by_version(
                 self.EXP_1_ID, [1, 2, 3, 4]))

        # TODO(#13059): After we fully type the codebase we plan to get
        # rid of the tests that intentionally test wrong inputs that we
        # can normally catch by typing.
        with self.assertRaisesRegex(  # type: ignore[no-untyped-call]
                ValueError, 'At least one version number is invalid'):
            (exp_fetchers.
             get_multiple_versioned_exp_interaction_ids_mapping_by_version(
                 self.EXP_1_ID, [1, 2, 2.5, 3]))  # type: ignore[list-item]
Exemple #40
0
    def test_handler_returns_correct_data(self):
        exp_services.update_exploration(self.owner_id, '0', [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name': exp_domain.STATE_PROPERTY_CONTENT,
                'state_name': 'Introduction',
                'new_value': {
                    'content_id': 'content',
                    'html': '<p>A content to translate.</p>'
                }
            })
        ], 'Changes content.')

        output = self.get_json('/gettranslatabletexthandler',
                               params={
                                   'language_code': 'hi',
                                   'exp_id': '0'
                               })

        expected_output = {
            'version': 2,
            'state_names_to_content_id_mapping': {
                'Introduction': {
                    'content': {
                        'content': ('<p>A content to translate.</p>'),
                        'data_format': 'html'
                    }
                },
                'End State': {
                    'content': {
                        'content': '',
                        'data_format': 'html'
                    }
                }
            }
        }

        self.assertEqual(output, expected_output)
    def setUp(self):
        super(UserContributionsOneOffJobTests, self).setUp()
        # User A has no created or edited explorations
        # User B has one created exploration
        # User C has one edited exploration
        # User D has created an exploration and then edited it.
        # (This is used to check that there are no duplicate
        # entries in the contribution lists.)
        self.signup(self.USER_A_EMAIL, self.USER_A_USERNAME)
        self.user_a_id = self.get_user_id_from_email(self.USER_A_EMAIL)
        self.signup(self.USER_B_EMAIL, self.USER_B_USERNAME)
        self.user_b_id = self.get_user_id_from_email(self.USER_B_EMAIL)
        self.signup(self.USER_C_EMAIL, self.USER_C_USERNAME)
        self.user_c_id = self.get_user_id_from_email(self.USER_C_EMAIL)
        self.signup(self.USER_D_EMAIL, self.USER_D_USERNAME)
        self.user_d_id = self.get_user_id_from_email(self.USER_D_EMAIL)

        self.save_new_valid_exploration(self.EXP_ID_1,
                                        self.user_b_id,
                                        end_state_name='End')

        exp_services.update_exploration(self.user_c_id, self.EXP_ID_1,
                                        [{
                                            'cmd': 'edit_exploration_property',
                                            'property_name': 'objective',
                                            'new_value': 'the objective'
                                        }], 'Test edit')

        self.save_new_valid_exploration(self.EXP_ID_2,
                                        self.user_d_id,
                                        end_state_name='End')

        exp_services.update_exploration(self.user_d_id, self.EXP_ID_2,
                                        [{
                                            'cmd': 'edit_exploration_property',
                                            'property_name': 'objective',
                                            'new_value': 'the objective'
                                        }], 'Test edit')
Exemple #42
0
    def test_get_exploration_version_history(self) -> None:
        version_history = exp_fetchers.get_exploration_version_history(
            self.EXP_1_ID, 2)

        self.assertIsNone(version_history)

        exp_services.update_exploration(  # type: ignore[no-untyped-call]
            self.owner_id, self.EXP_1_ID, [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_ADD_STATE,
                    'state_name': 'New state'
                })
            ], 'A commit message.')
        version_history = exp_fetchers.get_exploration_version_history(
            self.EXP_1_ID, 2)

        self.assertIsNotNone(version_history)
        if version_history is not None:
            self.assertEqual(version_history.committer_ids, [self.owner_id])
            self.assertEqual(
                version_history.state_version_history['New state'].to_dict(),
                state_domain.StateVersionHistory(None, None,
                                                 self.owner_id).to_dict())
Exemple #43
0
    def test_exploration_multiple_contributors(self):
        exploration = self._create_exploration(self.EXP_ID_1, self.user_a_id)
        # Give this exploration an average rating of 4.
        avg_rating = 4
        self._rate_exploration(exploration.id, 5, avg_rating)
        exp_services.update_exploration(self.user_b_id, self.EXP_ID_1, [], '')

        # The expected answer count is the sum of the first hit counts in the
        # statistics defined in _get_mock_statistics() method above.
        expected_answer_count = 15
        reach = expected_answer_count**self.EXPONENT
        contrib = 0.5
        expected_user_impact_score = python_utils.ROUND(
            ((avg_rating - 2) * reach * contrib)**self.EXPONENT)

        # Verify that the impact score matches the expected.
        self._run_computation()
        user_stats_model = user_models.UserStatsModel.get(self.user_a_id)
        self.assertEqual(user_stats_model.impact_score,
                         expected_user_impact_score)
        user_stats_model = user_models.UserStatsModel.get(self.user_b_id)
        self.assertEqual(user_stats_model.impact_score,
                         expected_user_impact_score)
    def test_missing_state_id_mapping_model_failure(self):
        exp_services.update_exploration(
            self.owner_id, '0', [exp_domain.ExplorationChange({
                'cmd': 'edit_exploration_property',
                'property_name': 'title',
                'new_value': 'New title'
            })], 'Changes.')
        exp_services.delete_state_id_mapping_model_for_exploration('0', 1)

        job = prod_validation_jobs_one_off.ProdValidationAuditOneOffJob
        job_id = job.create_new()
        job.enqueue(job_id)
        self.process_and_flush_pending_tasks()
        actual_output = job.get_output(job_id)
        expected_output = [
            (
                u'[u\'failed validation check for state_id_mapping_model '
                'field check of ExplorationModel\', '
                '[u"Model id 0: based on field state_id_mapping_model having '
                'value 0.1, expect model StateIdMappingModel with id 0.1 but '
                'it doesn\'t exist"]]'),
            u'[u\'fully-validated ExplorationModel\', 2]']
        self.assertEqual(sorted(actual_output), sorted(expected_output))
Exemple #45
0
    def setUp(self):
        """Create exploration with two versions"""
        super(VersioningIntegrationTest, self).setUp()

        self.EXP_ID = '0'

        exp_services.load_demo(self.EXP_ID)

        self.login(self.EDITOR_EMAIL)

        # In version 2, change the objective and the initial state content.
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(
            self.EDITOR_EMAIL, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'the objective',
            }, {
                'cmd': 'edit_state_property',
                'property_name': 'content',
                'state_name': exploration.init_state_name,
                'new_value': [{'type': 'text', 'value': 'ABC'}],
            }], 'Change objective and init state content')
Exemple #46
0
    def put(self, exploration_id):
        """Updates properties of the given exploration.

        Args:
            exploration_id: str. Id of exploration to be updated.

        Raises:
            InvalidInputException: The exploration update operation failed.
            PageNotFoundException: No exploration data exist for given user id
                and exploration id.
        """
        exploration = exp_services.get_exploration_by_id(exploration_id)
        version = self.payload.get('version')
        _require_valid_version(version, exploration.version)
        commit_message = self.payload.get('commit_message')
        change_list_dict = self.payload.get('change_list')
        change_list = [
            exp_domain.ExplorationChange(change) for change in change_list_dict
        ]

        try:
            exp_services.update_exploration(self.user_id,
                                            exploration_id,
                                            change_list,
                                            commit_message,
                                            is_by_voice_artist=True)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        try:
            exploration_data = exp_services.get_user_exploration_data(
                self.user_id, exploration_id)
        except:
            raise self.PageNotFoundException

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

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

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

        # Verify that the committer list does not contain the user
        # who only reverted.
        exploration_summary = exp_services.get_exploration_summary_by_id(
            exploration.id)
        self.assertEqual([user_a_id], exploration_summary.contributor_ids)
Exemple #48
0
    def test_get_text_to_create_voiceover_application_in_diff_language(self):
        exp_services.update_exploration(
            self.owner_id, '0', [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'property_name': (
                        exp_domain.STATE_PROPERTY_CONTENT),
                    'state_name': 'Introduction',
                    'new_value': {
                        'content_id': 'content',
                        'html': '<p>The new content to voiceover</p>'
                    }
                }), exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_ADD_TRANSLATION,
                    'state_name': 'Introduction',
                    'content_id': 'content',
                    'language_code': 'hi',
                    'content_html': '<p>The new content to voiceover</p>',
                    'translation_html': '<p>Translation in Hindi</p>'
                })], 'Adds new content to init state and its translation')

        content = voiceover_services.get_text_to_create_voiceover_application(
            suggestion_models.TARGET_TYPE_EXPLORATION, '0', 'hi')
        self.assertEqual(content, '<p>Translation in Hindi</p>')
Exemple #49
0
    def test_basic_computation_with_an_update_after_exploration_is_created(
            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'
            ANOTHER_USER_ID = 'another_user_id'

            self.save_new_valid_exploration(EXP_ID,
                                            USER_ID,
                                            title=EXP_TITLE,
                                            category='Category')
            # Another user makes a commit; this, too, shows up in the
            # original user's dashboard.
            exp_services.update_exploration(ANOTHER_USER_ID, EXP_ID, [],
                                            'Update exploration')
            expected_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(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([{
                'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                'last_updated_ms': expected_last_updated_ms,
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': ANOTHER_USER_ID,
                'subject': 'Update exploration',
            }], recent_notifications)
Exemple #50
0
    def setUp(self):
        """Create exploration with two versions"""
        super(VersioningIntegrationTest, self).setUp()

        exp_services.load_demo(self.EXP_ID)
        rights_manager.release_ownership_of_exploration(
            feconf.SYSTEM_COMMITTER_ID, self.EXP_ID)

        self.login(self.EDITOR_EMAIL)
        self.editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

        # In version 2, change the objective and the initial state content.
        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        exp_services.update_exploration(
            self.editor_id, self.EXP_ID, [{
                'cmd': 'edit_exploration_property',
                'property_name': 'objective',
                'new_value': 'the objective',
            }, {
                'cmd': 'edit_state_property',
                'property_name': 'content',
                'state_name': exploration.init_state_name,
                'new_value': [{'type': 'text', 'value': 'ABC'}],
            }], 'Change objective and init state content')
Exemple #51
0
    def test_multiple_computations_in_one_job(self):
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # setup example exploration
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            first_state_name = exp.init_state_name
            second_state_name = 'State 2'
            exp_services.update_exploration(
                '*****@*****.**', exp_id, [{
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': first_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'SetInput',
                }, {
                    'cmd': exp_domain.CMD_ADD_STATE,
                    'state_name': second_state_name,
                }, {
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': second_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'SetInput',
                }], 'Add new state')
            exp = exp_services.get_exploration_by_id(exp_id)
            exp_version = exp.version

            time_spent = 5.0
            params = {}

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

            # Run the aggregator job.
            ModifiedInteractionAnswerSummariesAggregator.start_computation()
            self.assertEqual(self.count_jobs_in_taskqueue(), 1)
            self.process_and_flush_pending_tasks()
            self.assertEqual(self.count_jobs_in_taskqueue(), 0)

            # Retrieve outputs for all of the computations running on this
            # interaction.
            answer_frequencies_calc_output_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id, first_state_name, 'Top10AnswerFrequencies'))
            self.assertEqual(
                'Top10AnswerFrequencies',
                answer_frequencies_calc_output_domain_object.calculation_id)

            common_elements_calc_output_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id, first_state_name,
                    'FrequencyCommonlySubmittedElements'))
            self.assertEqual(
                'FrequencyCommonlySubmittedElements',
                common_elements_calc_output_domain_object.calculation_id)

            calculation_output_first = (
                answer_frequencies_calc_output_domain_object.calculation_output
            )
            calculation_output_second = (
                common_elements_calc_output_domain_object.calculation_output)

            self.assertEqual(calculation_output_first,
                             [{
                                 'answer': ['answer1', 'answer2'],
                                 'frequency': 1
                             }])
            self.assertEqual(calculation_output_second, [{
                'answer': 'answer1',
                'frequency': 1
            }, {
                'answer': 'answer2',
                'frequency': 1
            }])
Exemple #52
0
    def test_answers_across_multiple_exploration_versions(self):
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # Setup example exploration.
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            first_state_name = exp.init_state_name
            second_state_name = 'State 2'
            exp_services.update_exploration(
                '*****@*****.**', exp_id, [{
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': first_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'MultipleChoiceInput',
                }, {
                    'cmd': exp_domain.CMD_ADD_STATE,
                    'state_name': second_state_name,
                }, {
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': second_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'MultipleChoiceInput',
                }], 'Add new state')
            exp = exp_services.get_exploration_by_id(exp_id)
            exp_version = exp.version

            time_spent = 5.0
            params = {}

            # 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')

            # Run the answers aggregation job.
            ModifiedInteractionAnswerSummariesAggregator.start_computation()
            self.assertEqual(self.count_jobs_in_taskqueue(), 1)
            self.process_and_flush_pending_tasks()
            self.assertEqual(self.count_jobs_in_taskqueue(), 0)

            calc_id = 'AnswerFrequencies'

            # Check the output of the job.
            calc_output_first_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id,
                    first_state_name,
                    calc_id,
                    exploration_version='2'))
            calc_output_all_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id, first_state_name, calc_id))

            self.assertEqual('AnswerFrequencies',
                             calc_output_first_domain_object.calculation_id)
            self.assertEqual('AnswerFrequencies',
                             calc_output_all_domain_object.calculation_id)

            calculation_output_first = (
                calc_output_first_domain_object.calculation_output)
            calculation_output_all = (
                calc_output_all_domain_object.calculation_output)

            expected_calculation_output_first_answer = [{
                'answer': 'answer1',
                'frequency': 1
            }]

            self.assertEqual(calculation_output_first,
                             expected_calculation_output_first_answer)
            self.assertEqual(calculation_output_all,
                             expected_calculation_output_first_answer)

            # Try modifying the exploration and adding another answer.
            exp_services.update_exploration(
                '*****@*****.**', exp_id, [{
                    'cmd': exp_domain.CMD_ADD_STATE,
                    'state_name': 'third state',
                }], 'Adding yet another state')
            exp = exp_services.get_exploration_by_id(exp_id)
            self.assertNotEqual(exp.version, exp_version)

            # Submit another answer.
            exp_version = exp.version
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, exp_version, first_state_name, 'MultipleChoiceInput',
                0, 0, exp_domain.EXPLICIT_CLASSIFICATION, 'session2',
                time_spent, params, 'answer1')

            # Run the aggregator again.
            ModifiedInteractionAnswerSummariesAggregator.stop_computation('a')
            ModifiedInteractionAnswerSummariesAggregator.start_computation()
            self.assertEqual(self.count_jobs_in_taskqueue(), 1)
            self.process_and_flush_pending_tasks()
            self.assertEqual(self.count_jobs_in_taskqueue(), 0)

            # Extract the output from the job.
            calc_output_first_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id,
                    first_state_name,
                    calc_id,
                    exploration_version='2'))
            calc_output_second_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id,
                    first_state_name,
                    calc_id,
                    exploration_version='3'))
            calc_output_all_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id, first_state_name, calc_id))

            self.assertEqual('AnswerFrequencies',
                             calc_output_first_domain_object.calculation_id)
            self.assertEqual('AnswerFrequencies',
                             calc_output_second_domain_object.calculation_id)
            self.assertEqual('AnswerFrequencies',
                             calc_output_all_domain_object.calculation_id)

            calculation_output_first = (
                calc_output_first_domain_object.calculation_output)
            calculation_output_second = (
                calc_output_second_domain_object.calculation_output)
            calculation_output_all = (
                calc_output_all_domain_object.calculation_output)

            # The output for version 2 of the exploration should be the same,
            # but the total combined output should include both answers. Also,
            # the output for version 3 should only include the second answer.
            expected_calculation_output_second_answer = [{
                'answer': 'answer1',
                'frequency': 1
            }]
            expected_calculation_output_all_answers = [{
                'answer': 'answer1',
                'frequency': 2
            }]

            self.assertEqual(calculation_output_first,
                             expected_calculation_output_first_answer)
            self.assertEqual(calculation_output_second,
                             expected_calculation_output_second_answer)
            self.assertEqual(calculation_output_all,
                             expected_calculation_output_all_answers)
Exemple #53
0
    def test_answers_across_multiple_exp_versions_different_interactions(self):
        """Similar to test_answers_across_multiple_exploration_versions except
        the exploration has changed interactions in the new versions. The
        aggregation job should not include answers corresponding to exploration
        versions which do not match the latest version's interaction ID.
        """
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # Setup example exploration.
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            init_state_name = exp.init_state_name

            time_spent = 5.0
            params = {}

            # Add a few different answers.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, '2')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')

            # Change the interaction ID.
            exp_services.update_exploration(
                '*****@*****.**', exp_id, [{
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'NumericInput',
                }], 'Change to NumericInput')

            # Submit an answer to the numeric interaction.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 2, init_state_name, 'NumericInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 2)

            # Change back the interaction ID.
            exp_services.update_exploration(
                '*****@*****.**', exp_id, [{
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'TextInput',
                }], 'Change to TextInput')

            # Submit another number-like answer.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 3, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, '2')

            # Create a 4th exploration version by changing the state's content.
            exp_services.update_exploration('*****@*****.**', exp_id, [{
                'cmd':
                exp_domain.CMD_EDIT_STATE_PROPERTY,
                'state_name':
                init_state_name,
                'property_name':
                exp_domain.STATE_PROPERTY_CONTENT,
                'new_value': [{
                    'type': 'text',
                    'value': 'New content description'
                }]
            }], 'Change content description')

            # Submit some more answers to the latest exploration version.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 4, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'noun')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 4, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 4, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'noun')

            exp = exp_services.get_exploration_by_id(exp_id)
            self.assertEqual(exp.version, 4)

            # Run the answers aggregation job.
            ModifiedInteractionAnswerSummariesAggregator.start_computation()
            self.assertEqual(self.count_jobs_in_taskqueue(), 1)
            self.process_and_flush_pending_tasks()
            self.assertEqual(self.count_jobs_in_taskqueue(), 0)

            calc_id = 'Top10AnswerFrequencies'

            # Check the output of the job.
            calc_output_latest_version_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id,
                    init_state_name,
                    calc_id,
                    exploration_version='4'))
            calc_output_all_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id, init_state_name, calc_id))

            self.assertEqual(
                'Top10AnswerFrequencies',
                calc_output_latest_version_domain_object.calculation_id)
            self.assertEqual('Top10AnswerFrequencies',
                             calc_output_all_domain_object.calculation_id)

            expected_calculation_latest_version_output = [{
                'answer': 'noun',
                'frequency': 2
            }, {
                'answer': 'verb',
                'frequency': 1
            }]

            # Only includes versions 3-4 since version 2 has a different
            # interaction ID.
            expected_calculation_all_versions_output = [{
                'answer': 'noun',
                'frequency': 2
            }, {
                'answer': '2',
                'frequency': 1
            }, {
                'answer': 'verb',
                'frequency': 1
            }]

            calculation_latest_version_output = (
                calc_output_latest_version_domain_object.calculation_output)
            calculation_output_all = (
                calc_output_all_domain_object.calculation_output)

            self.assertEqual(calculation_latest_version_output,
                             expected_calculation_latest_version_output)
            self.assertEqual(calculation_output_all,
                             expected_calculation_all_versions_output)
    def test_completing_translation_removes_language_from_incomplete_language_codes(  # pylint: disable=line-too-long
            self):
        story_services.update_story(self.owner_id, self.STORY_ID, [
            story_domain.StoryChange({
                'cmd': 'add_story_node',
                'node_id': 'node_1',
                'title': 'Node1',
            }),
            story_domain.StoryChange({
                'cmd': 'update_story_node_property',
                'property_name': 'exploration_id',
                'node_id': 'node_1',
                'old_value': None,
                'new_value': '0'
            })
        ], 'Changes.')
        translation_opportunities, _, _ = (
            opportunity_services.get_translation_opportunities('hi', None))
        self.assertEqual(len(translation_opportunities), 1)

        change_list = [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'state_name': 'Introduction',
                'property_name': 'content',
                'new_value': {
                    'html': '<p><strong>Test content</strong></p>',
                    'content_id': 'content',
                }
            }),
            exp_domain.ExplorationChange({
                'cmd':
                exp_domain.CMD_ADD_TRANSLATION,
                'state_name':
                'Introduction',
                'content_id':
                'content',
                'language_code':
                'hi',
                'content_html':
                '<p><strong>Test content</strong></p>',
                'translation_html':
                '<p>Translated text</p>'
            }),
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'state_name': 'End State',
                'property_name': 'content',
                'new_value': {
                    'html': '<p><strong>Test content</strong></p>',
                    'content_id': 'content',
                }
            }),
            exp_domain.ExplorationChange({
                'cmd':
                exp_domain.CMD_ADD_TRANSLATION,
                'state_name':
                'End State',
                'content_id':
                'content',
                'language_code':
                'hi',
                'content_html':
                '<p><strong>Test content</strong></p>',
                'translation_html':
                '<p>Translated text</p>'
            }),
        ]
        exp_services.update_exploration(self.owner_id, '0', change_list,
                                        'commit message')

        # get_translation_opportunities should no longer return the opportunity
        # after translation completion.
        translation_opportunities, _, _ = (
            opportunity_services.get_translation_opportunities('hi', None))
        self.assertEqual(len(translation_opportunities), 0)
Exemple #55
0
    def test_one_answer(self):
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # setup example exploration
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            first_state_name = exp.init_state_name
            second_state_name = 'State 2'
            exp_services.update_exploration(
                '*****@*****.**', exp_id, [{
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': first_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'MultipleChoiceInput',
                }, {
                    'cmd': exp_domain.CMD_ADD_STATE,
                    'state_name': second_state_name,
                }, {
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': second_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'MultipleChoiceInput',
                }], 'Add new state')
            exp = exp_services.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._record_start(exp_id, exp_version, first_state_name,
                               'session2')
            self.process_and_flush_pending_tasks()

            # add some answers
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, exp_version, first_state_name, 'MultipleChoiceInput',
                0, 0, exp_domain.EXPLICIT_CLASSIFICATION, 'session1',
                time_spent, params, 'answer1')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, exp_version, first_state_name, 'MultipleChoiceInput',
                0, 0, exp_domain.EXPLICIT_CLASSIFICATION, 'session2',
                time_spent, params, 'answer1')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, exp_version, first_state_name, 'MultipleChoiceInput',
                0, 0, exp_domain.EXPLICIT_CLASSIFICATION, 'session1',
                time_spent, params, 'answer2')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, exp_version, second_state_name, 'MultipleChoiceInput',
                0, 0, exp_domain.EXPLICIT_CLASSIFICATION, 'session2',
                time_spent, params, 'answer3')

            # Run job on exploration with answers
            ModifiedInteractionAnswerSummariesAggregator.start_computation()
            self.assertEqual(self.count_jobs_in_taskqueue(), 1)
            self.process_and_flush_pending_tasks()
            self.assertEqual(self.count_jobs_in_taskqueue(), 0)

            calc_id = 'AnswerFrequencies'

            # get job output of first state and check it
            calc_output_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id,
                    first_state_name,
                    calc_id,
                    exploration_version=exp_version))
            self.assertEqual('AnswerFrequencies',
                             calc_output_domain_object.calculation_id)

            calculation_output = calc_output_domain_object.calculation_output

            expected_calculation_output = [{
                'answer': 'answer1',
                'frequency': 2
            }, {
                'answer': 'answer2',
                'frequency': 1
            }]

            self.assertEqual(calculation_output, expected_calculation_output)

            # get job output of second state and check it
            calc_output_domain_object = (
                stats_jobs_continuous.InteractionAnswerSummariesAggregator.
                get_calc_output(  # pylint: disable=line-too-long
                    exp_id,
                    second_state_name,
                    calc_id,
                    exploration_version=exp_version))

            self.assertEqual('AnswerFrequencies',
                             calc_output_domain_object.calculation_id)

            calculation_output = calc_output_domain_object.calculation_output

            expected_calculation_output = [{
                'answer': 'answer3',
                'frequency': 1
            }]

            self.assertEqual(calculation_output, expected_calculation_output)
    def test_opportunity_updates_with_updating_exploration(self):
        self.add_exploration_0_to_story()

        translation_opportunities, _, _ = (
            opportunity_services.get_translation_opportunities('hi', None))
        self.assertEqual(len(translation_opportunities), 1)
        self.assertEqual(translation_opportunities[0].content_count, 2)

        answer_group_dict = {
            'outcome': {
                'dest': 'Introduction',
                'feedback': {
                    'content_id': 'feedback_1',
                    'html': '<p>Feedback</p>'
                },
                'labelled_as_correct': False,
                'param_changes': [],
                'refresher_exploration_id': None,
                'missing_prerequisite_skill_id': None
            },
            'rule_specs': [{
                'inputs': {
                    'x': 'Test'
                },
                'rule_type': 'Contains'
            }],
            'training_data': [],
            'tagged_skill_misconception_id': None
        }

        hints_list = []
        hints_list.append({
            'hint_content': {
                'content_id': 'hint_1',
                'html': '<p>hint one</p>'
            },
        })

        solution_dict = {
            'answer_is_exclusive': False,
            'correct_answer': 'helloworld!',
            'explanation': {
                'content_id': 'solution',
                'html': '<p>hello_world is a string</p>'
            },
        }
        exp_services.update_exploration(self.owner_id, '0', [
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                'state_name': 'Introduction',
                'new_value': 'TextInput'
            }),
            exp_domain.ExplorationChange({
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name':
                exp_domain.STATE_PROPERTY_INTERACTION_CUST_ARGS,
                'state_name': 'Introduction',
                'new_value': {
                    'placeholder': {
                        'value': {
                            'content_id': 'ca_placeholder_0',
                            'unicode_str': ''
                        }
                    },
                    'rows': {
                        'value': 1
                    }
                }
            }),
            exp_domain.ExplorationChange({
                'cmd':
                exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name':
                (exp_domain.STATE_PROPERTY_INTERACTION_ANSWER_GROUPS),
                'state_name':
                'Introduction',
                'new_value': [answer_group_dict]
            }),
            exp_domain.ExplorationChange({
                'cmd':
                exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name': (exp_domain.STATE_PROPERTY_INTERACTION_HINTS),
                'state_name':
                'Introduction',
                'new_value':
                hints_list
            }),
            exp_domain.ExplorationChange({
                'cmd':
                exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name':
                (exp_domain.STATE_PROPERTY_INTERACTION_SOLUTION),
                'state_name':
                'Introduction',
                'new_value':
                solution_dict
            })
        ], 'Add state name')
        translation_opportunities, _, _ = (
            opportunity_services.get_translation_opportunities('hi', None))
        self.assertEqual(len(translation_opportunities), 1)
        self.assertEqual(translation_opportunities[0].content_count, 5)
Exemple #57
0
    def test_answers_across_multiple_exp_versions_different_interactions(self):
        """Same as
        test_ignores_old_answers_if_new_interaction_has_no_new_answers except
        this also adds additional answers after changing the interaction a few
        times to ensure the aggregation job does not include answers across
        interaction changes, even if the interaction reverts back to a past
        interaction type with answers submitted to both versions of the
        exploration.
        """
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # Setup example exploration.
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            init_state_name = exp.init_state_name

            time_spent = 5.0
            params = {}

            # Add a few different answers.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, '2')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')

            # Change the interaction ID.
            exp_services.update_exploration('*****@*****.**', exp_id, [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'NumericInput',
                })
            ], 'Change to NumericInput')

            # Submit an answer to the numeric interaction.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 2, init_state_name, 'NumericInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 2)

            # Change back the interaction ID.
            exp_services.update_exploration('*****@*****.**', exp_id, [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'TextInput',
                })
            ], 'Change to TextInput')

            # Submit another number-like answer.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 3, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, '2')

            # Create a 4th exploration version by changing the state's content.
            exp_services.update_exploration('*****@*****.**', exp_id, [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_CONTENT,
                    'new_value': {
                        'content_id': 'content',
                        'html': '<p>New content description</p>'
                    }
                })
            ], 'Change content description')

            # Submit some more answers to the latest exploration version.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 4, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'noun')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 4, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 4, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'noun')

            exp = exp_fetchers.get_exploration_by_id(exp_id)
            self.assertEqual(exp.version, 4)

            # Run the answers aggregation job.
            MockInteractionAnswerSummariesAggregator.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)

            calc_id = 'Top10AnswerFrequencies'

            # Check the output of the job.
            calc_output_latest_version_model = self._get_calc_output_model(
                exp_id, init_state_name, calc_id, exploration_version='4')
            calc_output_all_model = self._get_calc_output_model(
                exp_id, init_state_name, calc_id)

            self.assertEqual('Top10AnswerFrequencies',
                             calc_output_latest_version_model.calculation_id)
            self.assertEqual('Top10AnswerFrequencies',
                             calc_output_all_model.calculation_id)

            expected_calculation_latest_version_output = [{
                'answer': 'noun',
                'frequency': 2
            }, {
                'answer': 'verb',
                'frequency': 1
            }]

            # Only includes versions 3-4 since version 2 has a different
            # interaction ID. Note that the output is dependent on the order of
            # submission (verb submitted before 2 -> verb ranked higher).
            expected_calculation_all_versions_output = [{
                'answer': 'noun',
                'frequency': 2
            }, {
                'answer': 'verb',
                'frequency': 1
            }, {
                'answer': '2',
                'frequency': 1
            }]

            calculation_latest_version_output = (
                calc_output_latest_version_model.calculation_output)
            calculation_output_all = calc_output_all_model.calculation_output

            self.assertEqual(calculation_latest_version_output,
                             expected_calculation_latest_version_output)
            self.assertEqual(calculation_output_all,
                             expected_calculation_all_versions_output)
Exemple #58
0
    def test_uses_old_answers_if_updated_exploration_has_same_interaction(
            self):
        """Similar to
        test_ignores_old_answers_if_new_interaction_has_no_new_answers except
        this is demonstrating that if an exploration is updated and no new
        answers are submitted to the new version, but the interaction ID is the
        same then old answers should still be aggregated.
        """
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # Setup example exploration.
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            init_state_name = exp.init_state_name

            time_spent = 5.0
            params = {}

            # Add a few different answers.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, '2')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')

            # Change something other than the interaction ID.
            exp_services.update_exploration('*****@*****.**', exp_id, [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_CONTENT,
                    'new_value': {
                        'content_id': 'content',
                        'html': '<p>New content</p>'
                    },
                })
            ], 'Change state content')

            exp = exp_fetchers.get_exploration_by_id(exp_id)
            self.assertEqual(exp.version, 2)

            # Run the answers aggregation job.
            MockInteractionAnswerSummariesAggregator.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)

            calc_id = 'Top10AnswerFrequencies'

            # Extract the output from the job.
            calc_output_model_latest_version = self._get_calc_output_model(
                exp_id, init_state_name, calc_id, exploration_version='2')
            calc_output_model_all = self._get_calc_output_model(
                exp_id, init_state_name, calc_id)

            # Since no answers were submitted to the latest version of the
            # exploration, there should be no calculated output for it.
            self.assertIsNone(calc_output_model_latest_version)

            self.assertEqual('Top10AnswerFrequencies',
                             calc_output_model_all.calculation_id)
            calculation_output_all = calc_output_model_all.calculation_output
            expected_calculation_output_all_answers = [{
                'answer': 'verb',
                'frequency': 2
            }, {
                'answer': '2',
                'frequency': 1
            }]
            self.assertEqual(calculation_output_all,
                             expected_calculation_output_all_answers)
Exemple #59
0
    def test_ignores_old_answers_if_new_interaction_has_no_new_answers(self):
        """Similar to test_answers_across_multiple_exploration_versions except
        the exploration has changed interactions in the new versions. The
        aggregation job should not include answers corresponding to exploration
        versions which do not match the latest version's interaction ID.
        """
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CC_MANAGERS_FOR_TESTS):

            # Setup example exploration.
            exp_id = 'eid'
            exp = self.save_new_valid_exploration(exp_id, '*****@*****.**')
            init_state_name = exp.init_state_name

            time_spent = 5.0
            params = {}

            # Add a few different answers.
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, '2')
            event_services.AnswerSubmissionEventHandler.record(
                exp_id, 1, init_state_name, 'TextInput', 0, 0,
                exp_domain.EXPLICIT_CLASSIFICATION, 'session1', time_spent,
                params, 'verb')

            # Change the interaction ID.
            exp_services.update_exploration('*****@*****.**', exp_id, [
                exp_domain.ExplorationChange({
                    'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                    'state_name': init_state_name,
                    'property_name': exp_domain.STATE_PROPERTY_INTERACTION_ID,
                    'new_value': 'NumericInput',
                })
            ], 'Change to NumericInput')

            exp = exp_fetchers.get_exploration_by_id(exp_id)
            self.assertEqual(exp.version, 2)

            # Run the answers aggregation job.
            MockInteractionAnswerSummariesAggregator.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)

            calc_id = 'Top10AnswerFrequencies'

            # Check the output of the job.
            calc_output_model_latest_version = self._get_calc_output_model(
                exp_id, init_state_name, calc_id, exploration_version='2')
            calc_output_model_first_version = self._get_calc_output_model(
                exp_id, init_state_name, calc_id, exploration_version='1')
            calc_output_model_all = self._get_calc_output_model(
                exp_id, init_state_name, calc_id)

            # Since no answers were submitted to the latest version of the
            # exploration, there should be no calculated output for it.
            self.assertIsNone(calc_output_model_latest_version)

            # Top answers will still be computed for the first version.
            self.assertEqual('Top10AnswerFrequencies',
                             calc_output_model_first_version.calculation_id)
            calculation_output_first = (
                calc_output_model_first_version.calculation_output)
            expected_calculation_output_first_answer = [{
                'answer': 'verb',
                'frequency': 2
            }, {
                'answer': '2',
                'frequency': 1
            }]
            self.assertEqual(calculation_output_first,
                             expected_calculation_output_first_answer)

            self.assertEqual('Top10AnswerFrequencies',
                             calc_output_model_all.calculation_id)

            # No answers should be aggregated since all past answers do not
            # match the newly submitted interaction ID.
            calculation_output_all = calc_output_model_all.calculation_output
            self.assertEqual(calculation_output_all, [])
    def test_basic_computation_ignores_automated_exploration_commits(self):
        with self._get_test_context():
            self.save_new_exp_with_states_schema_v0(EXP_ID, USER_ID, EXP_TITLE)

            # Confirm that the exploration is at version 1.
            exploration = exp_services.get_exploration_by_id(EXP_ID)
            self.assertEqual(exploration.version, 1)

            v1_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))

            # Start migration job on all explorations, including this one.
            job_id = (
                exp_jobs_one_off.ExplorationMigrationJobManager.create_new())
            exp_jobs_one_off.ExplorationMigrationJobManager.enqueue(job_id)
            self.process_and_flush_pending_tasks()

            # Confirm that the exploration is at version 2.
            exploration = exp_services.get_exploration_by_id(EXP_ID)
            self.assertEqual(exploration.version, 2)

            v2_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))

            # Run the aggregator.
            ModifiedRecentUpdatesAggregator.start_computation()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    queue_name=taskqueue_services.QUEUE_NAME_DEFAULT), 1)
            self.process_and_flush_pending_tasks()
            ModifiedRecentUpdatesAggregator.stop_computation(USER_ID)

            recent_notifications = (ModifiedRecentUpdatesAggregator.
                                    get_recent_notifications(USER_ID)[1])
            self.assertEqual(len(recent_notifications), 1)
            self.assertEqual(
                recent_notifications[0],
                self._get_expected_activity_created_dict(
                    USER_ID, EXP_ID, EXP_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT, v1_last_updated_ms))
            self.assertLess(recent_notifications[0]['last_updated_ms'],
                            v2_last_updated_ms)

            # Another user makes a commit; this one should now show up in the
            # original user's dashboard.
            exp_services.update_exploration(ANOTHER_USER_ID, EXP_ID, [],
                                            'Update exploration')
            v3_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(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([{
                'type': feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                'last_updated_ms': v3_last_updated_ms,
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': ANOTHER_USER_ID,
                'subject': 'Update exploration',
            }], recent_notifications)