def test_send_feedback_message_email(self):
        with self.can_send_emails_ctx, self.can_send_feedback_email_ctx:
            feedback_services.create_thread(
                self.exploration.id, 'a_state_name', self.user_id_a,
                'a subject', 'some text')
            threadlist = feedback_services.get_all_threads(
                self.exploration.id, False)
            thread_id = threadlist[0].get_thread_id()

            messagelist = feedback_services.get_messages(
                self.exploration.id, thread_id)
            self.assertEqual(len(messagelist), 1)

            expected_feedback_message_dict = {
                'exploration_id': self.exploration.id,
                'thread_id': thread_id,
                'message_id': messagelist[0].message_id
            }
            # There are two jobs in the taskqueue: one for the realtime event
            # associated with creating a thread, and one for sending the email.
            self.assertEqual(self.count_jobs_in_taskqueue(), 2)
            model = feedback_models.UnsentFeedbackEmailModel.get(self.editor_id)

            self.assertEqual(len(model.feedback_message_references), 1)
            self.assertDictEqual(
                model.feedback_message_references[0],
                expected_feedback_message_dict)
            self.assertEqual(model.retries, 0)
    def test_that_correct_emails_are_sent_for_multiple_feedback(self):
        expected_email_html_body = (
            'Hi editor,<br>'
            '<br>'
            'You\'ve received 1 new message on your Oppia explorations:<br>'
            '<ul><li>Title: some text<br></li>'
            '<li>Title: more text<br></li></ul>'
            'You can view and reply to your messages from your '
            '<a href="https://www.oppia.org/dashboard">dashboard</a>.'
            '<br>'
            'Thanks, and happy teaching!<br>'
            '<br>'
            'Best wishes,<br>'
            'The Oppia Team<br>'
            '<br>'
            'You can change your email preferences via the '
            '<a href="https://www.example.com">Preferences</a> page.')

        expected_email_text_body = (
            'Hi editor,\n'
            '\n'
            'You\'ve received 1 new message on your Oppia explorations:\n'
            '- Title: some text\n'
            '- Title: more text\n'
            'You can view and reply to your messages from your dashboard.'
            '\n'
            'Thanks, and happy teaching!\n'
            '\n'
            'Best wishes,\n'
            'The Oppia Team\n'
            '\n'
            'You can change your email preferences via the Preferences page.')

        with self.can_send_emails_ctx, self.can_send_feedback_email_ctx:
            feedback_services.create_thread(
                self.exploration.id, 'a_state_name',
                self.new_user_id, 'a subject', 'some text')

            threadlist = feedback_services.get_all_threads(
                self.exploration.id, False)
            thread_id = threadlist[0].get_thread_id()

            feedback_services.create_message(
                self.exploration.id, thread_id, self.new_user_id,
                feedback_models.STATUS_CHOICES_OPEN, 'subject', 'more text')

            messagelist = feedback_services.get_messages(
                self.exploration.id, thread_id)
            self.assertEqual(len(messagelist), 2)

            self.process_and_flush_pending_tasks()

            messages = self.mail_stub.get_sent_messages(to=self.EDITOR_EMAIL)
            self.assertEqual(len(messages), 1)
            self.assertEqual(
                messages[0].html.decode(),
                expected_email_html_body)
            self.assertEqual(
                messages[0].body.decode(),
                expected_email_text_body)
Esempio n. 3
0
    def test_feedback_ids(self):
        """Test various conventions for thread and message ids."""
        EXP_ID = '0'
        feedback_services.create_thread(
            EXP_ID, 'a_state_name', None, 'a subject', 'some text')
        threadlist = feedback_services.get_threadlist(EXP_ID)
        self.assertEqual(len(threadlist), 1)
        thread_id = threadlist[0]['thread_id']
        # The thread id should be prefixed with the exploration id and a full
        # stop.
        self.assertTrue(thread_id.startswith('%s.' % EXP_ID))
        # The rest of the thread id should not have any full stops.
        self.assertNotIn('.', thread_id[len(EXP_ID) + 1:])

        messages = feedback_services.get_messages(threadlist[0]['thread_id'])
        self.assertEqual(len(messages), 1)
        message_id = messages[0]['message_id']
        self.assertTrue(isinstance(message_id, int))

        # Retrieve the message instance from the storage layer.
        datastore_id = feedback_models.FeedbackMessageModel.get_messages(
            thread_id)[0].id
        # The message id should be prefixed with the thread id and a full stop,
        # followed by the message id.
        self.assertEqual(
            datastore_id, '%s.%s' % (thread_id, message_id))
Esempio n. 4
0
    def test_feedback_ids(self):
        """Test various conventions for thread and message ids."""
        exp_id = '0'
        feedback_services.create_thread(
            exp_id, 'a_state_name', None, 'a subject', 'some text')
        threadlist = feedback_services.get_all_threads(exp_id, False)
        self.assertEqual(len(threadlist), 1)
        thread_id = threadlist[0].get_thread_id()
        # The thread id should not have any full stops.
        self.assertNotIn('.', thread_id)

        messages = feedback_services.get_messages(exp_id, thread_id)
        self.assertEqual(len(messages), 1)
        message_id = messages[0].message_id
        self.assertTrue(isinstance(message_id, int))

        # Retrieve the message instance from the storage layer.
        datastore_id = feedback_models.FeedbackMessageModel.get_messages(
            exp_id, thread_id)[0].id

        full_thread_id = (feedback_models.FeedbackThreadModel
                          .generate_full_thread_id(exp_id, thread_id))
        # The message id should be prefixed with the full thread id and a full
        # stop, followed by the message id.
        self.assertEqual(
            datastore_id, '%s.%s' % (full_thread_id, message_id))
    def test_posting_to_feedback_thread_results_in_subscription(self):
        # The viewer posts a message to the thread.
        message_text = 'text'
        feedback_services.create_thread(
            'exp_id', 'state_name', self.viewer_id, 'subject', message_text)

        thread_ids_subscribed_to = self._get_thread_ids_subscribed_to(
            self.viewer_id)
        self.assertEqual(len(thread_ids_subscribed_to), 1)
        full_thread_id = thread_ids_subscribed_to[0]
        thread_id = (
            feedback_domain.FeedbackThread.get_thread_id_from_full_thread_id(
                full_thread_id))
        self.assertEqual(
            feedback_services.get_messages('exp_id', thread_id)[0].text,
            message_text)

        # The editor posts a follow-up message to the thread.
        new_message_text = 'new text'
        feedback_services.create_message(
            'exp_id', thread_id, self.editor_id, '', '', new_message_text)

        # The viewer and editor are now both subscribed to the thread.
        self.assertEqual(
            self._get_thread_ids_subscribed_to(self.viewer_id),
            [full_thread_id])
        self.assertEqual(
            self._get_thread_ids_subscribed_to(self.editor_id),
            [full_thread_id])
    def test_making_feedback_thread_does_not_subscribe_to_exploration(self):
        with self._get_test_context():
            self.signup(USER_A_EMAIL, USER_A_USERNAME)
            user_a_id = self.get_user_id_from_email(USER_A_EMAIL)
            self.signup(USER_B_EMAIL, USER_B_USERNAME)
            user_b_id = self.get_user_id_from_email(USER_B_EMAIL)

            # User A creates an exploration.
            self.save_new_valid_exploration(
                EXP_ID, user_a_id, title=EXP_TITLE, category='Category')
            exp_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))

            # User B starts a feedback thread.
            feedback_services.create_thread(
                EXP_ID, None, user_b_id, FEEDBACK_THREAD_SUBJECT, 'text')
            thread_id = feedback_services.get_all_threads(
                EXP_ID, False)[0].get_thread_id()

            message = feedback_services.get_messages(
                EXP_ID, thread_id)[0]

            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_for_user_a = (
                ModifiedRecentUpdatesAggregator.get_recent_notifications(
                    user_a_id)[1])
            recent_notifications_for_user_b = (
                ModifiedRecentUpdatesAggregator.get_recent_notifications(
                    user_b_id)[1])
            expected_thread_notification = {
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': user_b_id,
                'last_updated_ms': utils.get_time_in_millisecs(
                    message.created_on),
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }
            expected_creation_notification = (
                self._get_expected_activity_created_dict(
                    user_a_id, EXP_ID, EXP_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp_last_updated_ms))

            # User A sees A's commit and B's feedback thread.
            self.assertEqual(recent_notifications_for_user_a, [
                expected_thread_notification,
                expected_creation_notification
            ])
            # User B sees only her feedback thread, but no commits.
            self.assertEqual(recent_notifications_for_user_b, [
                expected_thread_notification,
            ])
Esempio n. 7
0
 def get(self, exploration_id, thread_id):  # pylint: disable=unused-argument
     suggestion = feedback_services.get_suggestion(exploration_id, thread_id)
     self.values.update({
         'messages': [m.to_dict() for m in feedback_services.get_messages(
             exploration_id, thread_id)],
         'suggestion': suggestion.to_dict() if suggestion else None
     })
     self.render_json(self.values)
Esempio n. 8
0
 def get(self, exploration_id, thread_id):  # pylint: disable=unused-argument
     self.values.update({
         'messages': feedback_services.get_messages(
             exploration_id, thread_id)})
     self.values.update({
         'suggestion': feedback_services.get_suggestion(
             exploration_id, thread_id)})
     self.render_json(self.values)
    def test_multiple_exploration_commits_and_feedback_messages(self):
        with self._get_test_context():
            self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
            editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

            # User creates an exploration.
            self.save_new_valid_exploration(
                EXP_1_ID, editor_id, title=EXP_1_TITLE,
                category='Category')

            exp1_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_1_ID))

            # User gives feedback on it.
            feedback_services.create_thread(
                EXP_1_ID, None, editor_id, FEEDBACK_THREAD_SUBJECT,
                'text')
            thread_id = feedback_services.get_all_threads(
                EXP_1_ID, False)[0].get_thread_id()
            message = feedback_services.get_messages(EXP_1_ID, thread_id)[0]

            # User creates another exploration.
            self.save_new_valid_exploration(
                EXP_2_ID, editor_id, title=EXP_2_TITLE,
                category='Category')
            exp2_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_2_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(
                    editor_id)[1])
            self.assertEqual([(
                self._get_expected_activity_created_dict(
                    editor_id, EXP_2_ID, EXP_2_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp2_last_updated_ms)
            ), {
                'activity_id': EXP_1_ID,
                'activity_title': EXP_1_TITLE,
                'author_id': editor_id,
                'last_updated_ms': utils.get_time_in_millisecs(
                    message.created_on),
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }, (
                self._get_expected_activity_created_dict(
                    editor_id, EXP_1_ID, EXP_1_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp1_last_updated_ms)
            )], recent_notifications)
    def test_posting_to_feedback_thread_results_in_subscription(self):
        # The viewer posts a message to the thread.
        MESSAGE_TEXT = "text"
        feedback_services.create_thread("exp_id", "state_name", self.viewer_id, "subject", MESSAGE_TEXT)

        thread_ids_subscribed_to = self._get_thread_ids_subscribed_to(self.viewer_id)
        self.assertEqual(len(thread_ids_subscribed_to), 1)
        thread_id = thread_ids_subscribed_to[0]
        self.assertEqual(feedback_services.get_messages(thread_id)[0]["text"], MESSAGE_TEXT)

        # The editor posts a follow-up message to the thread.
        NEW_MESSAGE_TEXT = "new text"
        feedback_services.create_message(thread_id, self.editor_id, "", "", NEW_MESSAGE_TEXT)

        # The viewer and editor are now both subscribed to the thread.
        self.assertEqual(self._get_thread_ids_subscribed_to(self.viewer_id), [thread_id])
        self.assertEqual(self._get_thread_ids_subscribed_to(self.editor_id), [thread_id])
Esempio n. 11
0
    def test_subscribing_to_exploration_subscribes_to_its_feedback_threads(self):
        with self.swap(
                jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                self.ALL_CONTINUOUS_COMPUTATION_MANAGERS_FOR_TESTS):
            EXP_ID = 'eid'
            EXP_TITLE = 'Title'
            FEEDBACK_THREAD_SUBJECT = 'feedback thread subject'

            USER_A_EMAIL = '*****@*****.**'
            USER_A_USERNAME = '******'
            self.signup(USER_A_EMAIL, USER_A_USERNAME)
            user_a_id = self.get_user_id_from_email(USER_A_EMAIL)

            USER_B_EMAIL = '*****@*****.**'
            USER_B_USERNAME = '******'
            self.signup(USER_B_EMAIL, USER_B_USERNAME)
            user_b_id = self.get_user_id_from_email(USER_B_EMAIL)

            # User A creates an exploration.
            self.save_new_valid_exploration(
                EXP_ID, user_a_id, title=EXP_TITLE, category='Category')
            exp_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))

            # User B starts a feedback thread.
            feedback_services.create_thread(
                EXP_ID, None, user_b_id, FEEDBACK_THREAD_SUBJECT, 'text')
            thread_id = (
                feedback_services.get_threadlist(EXP_ID)[0]['thread_id'])
            message = feedback_services.get_messages(thread_id)[0]

            # User A adds user B as an editor of the exploration.
            rights_manager.assign_role_for_exploration(
                user_a_id, EXP_ID, user_b_id, rights_manager.ROLE_EDITOR)

            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_for_user_a = (
                ModifiedRecentUpdatesAggregator.get_recent_notifications(
                    user_a_id)[1])
            recent_notifications_for_user_b = (
                ModifiedRecentUpdatesAggregator.get_recent_notifications(
                    user_b_id)[1])
            expected_feedback_thread_notification_dict = {
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': user_b_id,
                'last_updated_ms': message['created_on'],
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }
            expected_exploration_created_notification_dict = (
                self._get_expected_activity_created_dict(
                    user_a_id, EXP_ID, EXP_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp_last_updated_ms))

            # User A sees A's commit and B's feedback thread.
            self.assertEqual(recent_notifications_for_user_a, [
                expected_feedback_thread_notification_dict,
                expected_exploration_created_notification_dict
            ])
            # User B sees A's commit and B's feedback thread.
            self.assertEqual(recent_notifications_for_user_b, [
                expected_feedback_thread_notification_dict,
                expected_exploration_created_notification_dict,
            ])
Esempio n. 12
0
    def test_multiple_exploration_commits_and_feedback_messages(self):
        with self.swap(
                jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                self.ALL_CONTINUOUS_COMPUTATION_MANAGERS_FOR_TESTS):
            EXP_1_ID = 'eid1'
            EXP_1_TITLE = 'Title1'
            EXP_2_ID = 'eid2'
            EXP_2_TITLE = 'Title2'
            FEEDBACK_THREAD_SUBJECT = 'feedback thread subject'

            self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
            self.EDITOR_ID = self.get_user_id_from_email(self.EDITOR_EMAIL)

            # User creates an exploration.
            self.save_new_valid_exploration(
                EXP_1_ID, self.EDITOR_ID, title=EXP_1_TITLE,
                category='Category')

            exp1_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_1_ID))

            # User gives feedback on it.
            feedback_services.create_thread(
                EXP_1_ID, None, self.EDITOR_ID, FEEDBACK_THREAD_SUBJECT,
                'text')
            thread_id = (
                feedback_services.get_threadlist(EXP_1_ID)[0]['thread_id'])
            message = feedback_services.get_messages(thread_id)[0]

            # User creates another exploration.
            self.save_new_valid_exploration(
                EXP_2_ID, self.EDITOR_ID, title=EXP_2_TITLE,
                category='Category')
            exp2_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_2_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(
                    self.EDITOR_ID)[1])
            self.assertEqual([(
                self._get_expected_activity_created_dict(
                    self.EDITOR_ID, EXP_2_ID, EXP_2_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp2_last_updated_ms)
            ), {
                'activity_id': EXP_1_ID,
                'activity_title': EXP_1_TITLE,
                'author_id': self.EDITOR_ID,
                'last_updated_ms': message['created_on'],
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }, (
                self._get_expected_activity_created_dict(
                    self.EDITOR_ID, EXP_1_ID, EXP_1_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp1_last_updated_ms)
            )], recent_notifications)
Esempio n. 13
0
    def test_making_feedback_thread_does_not_subscribe_to_exploration(self):
        with self.swap(user_jobs_continuous,
                       'DashboardRecentUpdatesAggregator',
                       MockRecentUpdatesAggregator):
            self.signup(USER_A_EMAIL, USER_A_USERNAME)
            user_a_id = self.get_user_id_from_email(USER_A_EMAIL)
            self.signup(USER_B_EMAIL, USER_B_USERNAME)
            user_b_id = self.get_user_id_from_email(USER_B_EMAIL)

            # User A creates an exploration.
            self.save_new_valid_exploration(EXP_ID,
                                            user_a_id,
                                            title=EXP_TITLE,
                                            category='Category')
            exp_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))

            # User B starts a feedback thread.
            feedback_services.create_thread('exploration', EXP_ID, user_b_id,
                                            FEEDBACK_THREAD_SUBJECT, 'text')
            thread_id = feedback_services.get_all_threads(
                'exploration', EXP_ID, False)[0].id

            message = feedback_services.get_messages(thread_id)[0]

            (user_jobs_continuous.DashboardRecentUpdatesAggregator.
             start_computation())
            self.assertEqual(
                self.count_jobs_in_mapreduce_taskqueue(
                    taskqueue_services.QUEUE_NAME_CONTINUOUS_JOBS), 1)
            self.process_and_flush_pending_mapreduce_tasks()

            recent_notifications_for_user_a = (
                user_jobs_continuous.DashboardRecentUpdatesAggregator.
                get_recent_user_changes(user_a_id)[1])
            recent_notifications_for_user_b = (
                user_jobs_continuous.DashboardRecentUpdatesAggregator.
                get_recent_user_changes(user_b_id)[1])
            expected_thread_notification = {
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': user_b_id,
                'last_updated_ms':
                utils.get_time_in_millisecs(message.created_on),
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }
            expected_creation_notification = (
                self._get_expected_activity_created_dict(
                    user_a_id, EXP_ID, EXP_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp_last_updated_ms))

            # User A sees A's commit and B's feedback thread.
            self.assertEqual(
                recent_notifications_for_user_a,
                [expected_thread_notification, expected_creation_notification])
            # User B sees only her feedback thread, but no commits.
            self.assertEqual(recent_notifications_for_user_b, [
                expected_thread_notification,
            ])
Esempio n. 14
0
    def test_subscribing_to_exp_subscribes_to_its_feedback_threads(self):
        with self.swap(jobs_registry, 'ALL_CONTINUOUS_COMPUTATION_MANAGERS',
                       self.ALL_CONTINUOUS_COMPUTATION_MANAGERS_FOR_TESTS):
            EXP_ID = 'eid'
            EXP_TITLE = 'Title'
            FEEDBACK_THREAD_SUBJECT = 'feedback thread subject'

            USER_A_EMAIL = '*****@*****.**'
            USER_A_USERNAME = '******'
            self.signup(USER_A_EMAIL, USER_A_USERNAME)
            user_a_id = self.get_user_id_from_email(USER_A_EMAIL)

            USER_B_EMAIL = '*****@*****.**'
            USER_B_USERNAME = '******'
            self.signup(USER_B_EMAIL, USER_B_USERNAME)
            user_b_id = self.get_user_id_from_email(USER_B_EMAIL)

            # User A creates an exploration.
            self.save_new_valid_exploration(EXP_ID,
                                            user_a_id,
                                            title=EXP_TITLE,
                                            category='Category')
            exp_last_updated_ms = utils.get_time_in_millisecs(
                exp_services.get_exploration_by_id(EXP_ID).last_updated)

            # User B starts a feedback thread.
            feedback_services.create_thread(EXP_ID, None, user_b_id,
                                            FEEDBACK_THREAD_SUBJECT, 'text')
            thread_id = (
                feedback_services.get_threadlist(EXP_ID)[0]['thread_id'])
            message = feedback_services.get_messages(thread_id)[0]

            # User A adds user B as an editor of the exploration.
            rights_manager.assign_role(user_a_id, EXP_ID, user_b_id,
                                       rights_manager.ROLE_EDITOR)

            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_for_user_a = (ModifiedRecentUpdatesAggregator.
                                         get_recent_updates(user_a_id)[1])
            recent_updates_for_user_b = (ModifiedRecentUpdatesAggregator.
                                         get_recent_updates(user_b_id)[1])
            expected_feedback_thread_update_dict = {
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': user_b_id,
                'last_updated_ms': message['created_on'],
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }
            expected_exploration_created_update_dict = (
                self._get_expected_exploration_created_dict(
                    user_a_id, EXP_ID, EXP_TITLE, exp_last_updated_ms))

            # User A sees A's commit and B's feedback thread.
            self.assertEqual(recent_updates_for_user_a, [
                expected_feedback_thread_update_dict,
                expected_exploration_created_update_dict
            ])
            # User B sees A's commit and B's feedback thread.
            self.assertEqual(recent_updates_for_user_b, [
                expected_feedback_thread_update_dict,
                expected_exploration_created_update_dict,
            ])
    def test_subscribing_to_exploration_subscribes_to_its_feedback_threads(
            self):
        with self._get_test_context():
            self.signup(USER_A_EMAIL, USER_A_USERNAME)
            user_a_id = self.get_user_id_from_email(USER_A_EMAIL)
            self.signup(USER_B_EMAIL, USER_B_USERNAME)
            user_b_id = self.get_user_id_from_email(USER_B_EMAIL)
            user_a = user_services.UserActionsInfo(user_a_id)

            # User A creates an exploration.
            self.save_new_valid_exploration(EXP_ID,
                                            user_a_id,
                                            title=EXP_TITLE,
                                            category='Category')
            exp_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_ID))

            # User B starts a feedback thread.
            feedback_services.create_thread('exploration', EXP_ID, user_b_id,
                                            FEEDBACK_THREAD_SUBJECT, 'text')
            thread_id = feedback_services.get_all_threads(
                'exploration', EXP_ID, False)[0].id
            message = feedback_services.get_messages(thread_id)[0]

            # User A adds user B as an editor of the exploration.
            rights_manager.assign_role_for_exploration(
                user_a, EXP_ID, user_b_id, rights_manager.ROLE_EDITOR)

            MockRecentUpdatesAggregator.start_computation()
            self.assertEqual(
                self.count_jobs_in_taskqueue(
                    taskqueue_services.QUEUE_NAME_CONTINUOUS_JOBS), 1)
            self.process_and_flush_pending_tasks()

            recent_notifications_for_user_a = (
                MockRecentUpdatesAggregator.get_recent_notifications(
                    user_a_id)[1])
            recent_notifications_for_user_b = (
                MockRecentUpdatesAggregator.get_recent_notifications(
                    user_b_id)[1])
            expected_thread_notification = {
                'activity_id': EXP_ID,
                'activity_title': EXP_TITLE,
                'author_id': user_b_id,
                'last_updated_ms':
                utils.get_time_in_millisecs(message.created_on),
                'subject': FEEDBACK_THREAD_SUBJECT,
                'type': feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
            }
            expected_creation_notification = (
                self._get_expected_activity_created_dict(
                    user_a_id, EXP_ID, EXP_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp_last_updated_ms))

            # User A sees A's commit and B's feedback thread.
            self.assertEqual(
                recent_notifications_for_user_a,
                [expected_thread_notification, expected_creation_notification])
            # User B sees A's commit and B's feedback thread.
            self.assertEqual(recent_notifications_for_user_b, [
                expected_thread_notification,
                expected_creation_notification,
            ])
Esempio n. 16
0
    def test_email_sent_when_feedback_in_thread(self):
        # Create feedback thread.
        with self.can_send_feedback_email_ctx, self.can_send_emails_ctx:
            feedback_services.create_thread(feconf.ENTITY_TYPE_EXPLORATION,
                                            self.exploration.id,
                                            self.user_id_a, 'a subject',
                                            'some text')
            threadlist = feedback_services.get_all_threads(
                feconf.ENTITY_TYPE_EXPLORATION, self.exploration.id, False)
            thread_id = threadlist[0].id

            # Create another message.
            feedback_services.create_message(thread_id, self.user_id_b, None,
                                             None, 'user b message')

            # Check that there are two messages in thread.
            messages = feedback_services.get_messages(thread_id)
            self.assertEqual(len(messages), 2)

            # Check that there are no feedback emails sent to Editor.
            messages = self._get_sent_email_messages(self.EDITOR_EMAIL)
            self.assertEqual(len(messages), 0)

            # Send task and subsequent email to Editor.
            self.process_and_flush_pending_tasks()
            messages = self._get_sent_email_messages(self.EDITOR_EMAIL)
            expected_message = (
                'Hi editor,\n\nYou\'ve received 2 new messages on your'
                ' Oppia explorations:\n- Title:\n- some text\n- user b message'
                '\nYou can view and reply to your messages from your dashboard.'
                '\n\nThanks, and happy teaching!\n\nBest wishes,\nThe Oppia'
                ' Team\n\nYou can change your email preferences via the '
                'Preferences page.')

            # Assert that the message is correct.
            self.assertEqual(len(messages), 1)
            self.assertEqual(messages[0].body.decode(), expected_message)

            # Create another message that is len = 201.
            user_b_message = 'B' * 201
            feedback_services.create_message(thread_id, self.user_id_b, None,
                                             None, user_b_message)

            # Check that there are three messages in thread.
            messages = feedback_services.get_messages(thread_id)
            self.assertEqual(len(messages), 3)

            # Send task and subsequent email to Editor.
            self.process_and_flush_pending_tasks()
            messages = self._get_sent_email_messages(self.EDITOR_EMAIL)

            # What is expected in the email body.
            expected_message = (
                'Hi editor,\n\nYou\'ve received a new message on your Oppia'
                ' explorations:\n- Title:\n- ' + 'B' * 200 + '...' +
                '\nYou can'
                ' view and reply to your messages from your dashboard.\n\nThank'
                's, and happy teaching!\n\nBest wishes,\nThe Oppia Team\n\nYou'
                ' can change your email preferences via the Preferences page.')

            # Check that greater than 200 word message is sent
            # and has correct message.

            self.assertEqual(len(messages), 2)
            self.assertEqual(messages[1].body.decode(), expected_message)
Esempio n. 17
0
    def test_multiple_exploration_commits_and_feedback_messages(self):
        with self.swap(user_jobs_continuous,
                       'DashboardRecentUpdatesAggregator',
                       MockRecentUpdatesAggregator):
            self.signup(self.EDITOR_EMAIL, self.EDITOR_USERNAME)
            editor_id = self.get_user_id_from_email(self.EDITOR_EMAIL)

            # User creates an exploration.
            self.save_new_valid_exploration(EXP_1_ID,
                                            editor_id,
                                            title=EXP_1_TITLE,
                                            category='Category')

            exp1_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_1_ID))

            # User gives feedback on it.
            feedback_services.create_thread('exploration', EXP_1_ID, editor_id,
                                            FEEDBACK_THREAD_SUBJECT, 'text')
            thread_id = feedback_services.get_all_threads(
                'exploration', EXP_1_ID, False)[0].id
            message = feedback_services.get_messages(thread_id)[0]

            # User creates another exploration.
            self.save_new_valid_exploration(EXP_2_ID,
                                            editor_id,
                                            title=EXP_2_TITLE,
                                            category='Category')
            exp2_last_updated_ms = (
                self._get_most_recent_exp_snapshot_created_on_ms(EXP_2_ID))

            (user_jobs_continuous.DashboardRecentUpdatesAggregator.
             start_computation())
            self.assertEqual(
                self.count_jobs_in_mapreduce_taskqueue(
                    taskqueue_services.QUEUE_NAME_CONTINUOUS_JOBS), 1)
            self.process_and_flush_pending_mapreduce_tasks()

            recent_notifications = (
                user_jobs_continuous.DashboardRecentUpdatesAggregator.
                get_recent_user_changes(editor_id)[1])
            self.assertEqual(
                [(self._get_expected_activity_created_dict(
                    editor_id, EXP_2_ID, EXP_2_TITLE, 'exploration',
                    feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                    exp2_last_updated_ms)), {
                        'activity_id':
                        EXP_1_ID,
                        'activity_title':
                        EXP_1_TITLE,
                        'author_id':
                        editor_id,
                        'last_updated_ms':
                        utils.get_time_in_millisecs(message.created_on),
                        'subject':
                        FEEDBACK_THREAD_SUBJECT,
                        'type':
                        feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
                    },
                 (self._get_expected_activity_created_dict(
                     editor_id, EXP_1_ID, EXP_1_TITLE, 'exploration',
                     feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                     exp1_last_updated_ms))], recent_notifications)
Esempio n. 18
0
    def _get_message_ids_in_a_thread(self, thread_id):
        messages = feedback_services.get_messages(thread_id)

        return [message.message_id for message in messages]
Esempio n. 19
0
 def get(self, exploration_id, thread_id):
     self.values.update({"messages": feedback_services.get_messages(thread_id)})
     self.render_json(self.values)
Esempio n. 20
0
    def test_email_sent_when_feedback_in_thread(self):
        # Create feedback thread.
        with self.can_send_feedback_email_ctx, self.can_send_emails_ctx:
            feedback_services.create_thread(feconf.ENTITY_TYPE_EXPLORATION,
                                            self.exploration.id,
                                            self.user_id_a, 'a subject',
                                            'some text')
            threadlist = feedback_services.get_all_threads(
                feconf.ENTITY_TYPE_EXPLORATION, self.exploration.id, False)
            thread_id = threadlist[0].id

            # Create another message.
            feedback_services.create_message(thread_id, self.user_id_b, None,
                                             None, 'user b message')

            # Check that there are two messages in thread.
            messages = feedback_services.get_messages(thread_id)
            self.assertEqual(len(messages), 2)

            # Telling tasks.py to send email to User 'A'.
            payload = {'user_id': self.user_id_a}
            taskqueue_services.enqueue_email_task(
                feconf.TASK_URL_FEEDBACK_MESSAGE_EMAILS, payload, 0)

            # Check that there are no feedback emails sent to User 'A'.
            messages = self.mail_stub.get_sent_messages(to=self.USER_A_EMAIL)
            self.assertEqual(len(messages), 0)

            # Send task and subsequent email to User 'A'.
            self.process_and_flush_pending_tasks()
            messages = self.mail_stub.get_sent_messages(to=self.USER_A_EMAIL)
            expected_message = (
                'Hi userA,\n\nNew update to thread "a subject"'
                ' on Title:\n- userB: user b message\n(You received'
                ' this message because you are a participant in this thread.)'
                '\n\nBest wishes,\nThe Oppia team\n\nYou can change your email'
                ' preferences via the Preferences page.')

            # Assert that the message is correct.
            self.assertEqual(len(messages), 1)
            self.assertEqual(messages[0].body.decode(), expected_message)

            # Create another message that is len = 201.
            user_b_message = 'B' * 201
            feedback_services.create_message(thread_id, self.user_id_b, None,
                                             None, user_b_message)

            # Check that there are three messages in thread.
            messages = feedback_services.get_messages(thread_id)
            self.assertEqual(len(messages), 3)

            # Telling tasks.py to send email to User 'A'.
            payload = {'user_id': self.user_id_a}
            taskqueue_services.enqueue_email_task(
                feconf.TASK_URL_FEEDBACK_MESSAGE_EMAILS, payload, 0)

            # Check that there is one feedback email sent to User 'A'.
            messages = self.mail_stub.get_sent_messages(to=self.USER_A_EMAIL)
            self.assertEqual(len(messages), 1)

            # Send task and subsequent email to User 'A'.
            self.process_and_flush_pending_tasks()
            messages = self.mail_stub.get_sent_messages(to=self.USER_A_EMAIL)

            # What is expected in the email body.
            expected_message = (
                'Hi userA,\n\nNew update to thread "a subject"'
                ' on Title:\n- userB:' + 'B' * 200 + '...' +
                ' e\n(You received'
                ' this message because you are a participant in this thread.)'
                '\n\nBest wishes,\nThe Oppia team\n\nYou can change your email'
                ' preferences via the Preferences page.')

            # Check that greater than 200 word message is sent.
            self.assertEqual(len(messages), 2)