Beispiel #1
0
    def test_get_more_threads_on_request(self):
        self.login(self.OWNER_EMAIL)

        csrf_token = self.get_new_csrf_token()
        self.save_new_default_exploration(self.EXP_ID_1,
                                          self.owner_id,
                                          title=self.EXP_TITLE_1)
        for i in range(190):
            feedback_services.create_thread('exploration', self.EXP_ID_1,
                                            self.owner_id, 'a subject %s' % i,
                                            'some text %s' % i)

        response = self.post_json(
            feconf.LEARNER_DASHBOARD_FEEDBACK_UPDATES_DATA_URL,
            {'paginated_threads_list': []},
            csrf_token=csrf_token,
            expected_status_int=200)
        thread_summaries = response['thread_summaries']
        thread_id = thread_summaries[0]['thread_id']
        thread = feedback_services.get_thread(thread_id)
        paginated_threads_list = response['paginated_threads_list']

        self.assertEqual(len(paginated_threads_list), 1)
        self.assertEqual(len(paginated_threads_list[0]), 90)
        self.assertEqual(len(thread_summaries), 100)
        self.assertEqual(thread_summaries[0]['total_message_count'], 1)
        self.assertEqual(thread_summaries[0]['exploration_title'],
                         self.EXP_TITLE_1)
        self.assertEqual(thread_summaries[0]['exploration_id'], self.EXP_ID_1)
        self.assertEqual(thread_summaries[0]['last_message_text'],
                         'some text 0')
        self.assertEqual(thread_summaries[0]['original_author_id'],
                         self.owner_id)
        self.assertEqual(thread.subject, 'a subject 0')
        self.assertEqual(thread.entity_type, 'exploration')

        response = self.post_json(
            feconf.LEARNER_DASHBOARD_FEEDBACK_UPDATES_DATA_URL,
            {'paginated_threads_list': paginated_threads_list},
            csrf_token=csrf_token,
            expected_status_int=200)
        thread_summaries = response['thread_summaries']
        thread_id = thread_summaries[0]['thread_id']
        thread = feedback_services.get_thread(thread_id)
        paginated_threads_list = response['paginated_threads_list']

        self.assertEqual(len(response['paginated_threads_list']), 0)
        self.assertEqual(len(thread_summaries), 90)
        self.assertEqual(thread_summaries[0]['total_message_count'], 1)
        self.assertEqual(thread_summaries[0]['exploration_title'],
                         self.EXP_TITLE_1)
        self.assertEqual(thread_summaries[0]['exploration_id'], self.EXP_ID_1)
        self.assertEqual(thread_summaries[0]['last_message_text'],
                         'some text 100')
        self.assertEqual(thread_summaries[0]['original_author_id'],
                         self.owner_id)
        self.assertEqual(thread.subject, 'a subject 100')
        self.assertEqual(thread.entity_type, 'exploration')
        self.logout()
    def test_get_threads_after_updating_thread_summaries(self):
        self.login(self.OWNER_EMAIL)

        response = self.get_json(feconf.LEARNER_DASHBOARD_DATA_URL)
        thread_summaries = response['thread_summaries']
        self.assertEqual(thread_summaries, [])

        self.save_new_default_exploration(self.EXP_ID_1,
                                          self.owner_id,
                                          title=self.EXP_TITLE_1)
        feedback_services.create_thread('exploration', self.EXP_ID_1,
                                        self.owner_id, 'a subject',
                                        'some text')

        response = self.get_json(feconf.LEARNER_DASHBOARD_DATA_URL)
        thread_summaries = response['thread_summaries']
        thread_id = thread_summaries[0]['thread_id']
        thread = feedback_services.get_thread(thread_id)

        self.assertEqual(len(thread_summaries), 1)
        self.assertEqual(thread_summaries[0]['total_message_count'], 1)
        self.assertEqual(thread_summaries[0]['exploration_title'],
                         self.EXP_TITLE_1)
        self.assertEqual(thread_summaries[0]['exploration_id'], self.EXP_ID_1)
        self.assertEqual(thread_summaries[0]['last_message_text'], 'some text')
        self.assertEqual(thread_summaries[0]['original_author_id'],
                         self.owner_id)
        self.assertEqual(thread.subject, 'a subject')
        self.assertEqual(thread.entity_type, 'exploration')
        self.logout()
Beispiel #3
0
    def post(self):
        payload = json.loads(self.request.body)
        user_id = payload['user_id']
        reference_dict = payload['reference_dict']

        message = feedback_services.get_message(reference_dict['thread_id'],
                                                reference_dict['message_id'])
        exploration = exp_fetchers.get_exploration_by_id(
            reference_dict['entity_id'])
        thread = feedback_services.get_thread(reference_dict['thread_id'])
        model = email_models.GeneralFeedbackEmailReplyToIdModel.get(
            user_id, reference_dict['thread_id'])
        reply_to_id = model.reply_to_id

        subject = 'New Oppia message in "%s"' % thread.subject
        email_manager.send_instant_feedback_message_email(
            user_id,
            message.author_id,
            message.text,
            subject,
            exploration.title,
            reference_dict['entity_id'],
            thread.subject,
            reply_to_id=reply_to_id)
        self.render_json({})
Beispiel #4
0
    def post(self):
        payload = json.loads(self.request.body)
        user_id = payload['user_id']
        reference_dict = payload['reference_dict']

        message = feedback_services.get_message(reference_dict['thread_id'],
                                                reference_dict['message_id'])
        exploration = exp_fetchers.get_exploration_by_id(
            reference_dict['entity_id'])
        thread = feedback_services.get_thread(reference_dict['thread_id'])
        feedback_thread_reply_info = (
            email_services.get_feedback_thread_reply_info_by_user_and_thread(
                user_id, reference_dict['thread_id']))
        if feedback_thread_reply_info is None:
            raise self.InvalidInputException(
                'Feedback thread for current user and thread_id does not exist'
            )

        subject = 'New Oppia message in "%s"' % thread.subject
        email_manager.send_instant_feedback_message_email(
            user_id,
            message.author_id,
            message.text,
            subject,
            exploration.title,
            reference_dict['entity_id'],
            thread.subject,
            reply_to_id=feedback_thread_reply_info.reply_to_id)
        self.render_json({})
Beispiel #5
0
    def test_get_suggestions_after_updating_suggestion_summary(self):
        self.login(self.EDITOR_EMAIL)

        response_dict = self.get_json(
            '%s/%s' %
            (feconf.FEEDBACK_THREADLIST_URL_PREFIX, self.EXP_ID_1)
        )
        thread_id = response_dict['feedback_thread_dicts'][0]['thread_id']
        thread_url = '%s/%s' % (
            feconf.LEARNER_DASHBOARD_FEEDBACK_THREAD_DATA_URL, thread_id)
        response_dict = self.get_json(thread_url)
        messages_summary = response_dict['message_summary_list'][0]

        self.assertEqual(
            messages_summary['author_username'], self.EDITOR_USERNAME)
        self.assertTrue(test_utils.check_image_png_or_webp(
            messages_summary['author_picture_data_url']))
        self.assertFalse(messages_summary.get('suggestion_html'))
        self.assertFalse(messages_summary.get('current_content_html'))
        self.assertFalse(messages_summary.get('description'))

        new_content = state_domain.SubtitledHtml(
            'content', '<p>new content html</p>').to_dict()
        change_cmd = {
            'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
            'property_name': exp_domain.STATE_PROPERTY_CONTENT,
            'state_name': 'Welcome!',
            'new_value': new_content
        }

        suggestion_models.GeneralSuggestionModel.create(
            feconf.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
            feconf.ENTITY_TYPE_EXPLORATION, self.EXP_ID_1, 1,
            suggestion_models.STATUS_IN_REVIEW, self.editor_id, None,
            change_cmd, 'score category', thread_id, None)

        suggestion_thread = feedback_services.get_thread(thread_id)
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        exploration = exp_fetchers.get_exploration_by_id(self.EXP_ID_1)
        current_content_html = (
            exploration.states[
                suggestion.change.state_name].content.html)
        response_dict = self.get_json(thread_url)
        messages_summary = response_dict['message_summary_list'][0]
        first_suggestion = feedback_services.get_messages(thread_id)[0]

        self.assertEqual(
            messages_summary['author_username'], self.EDITOR_USERNAME)
        self.assertTrue(test_utils.check_image_png_or_webp(
            messages_summary['author_picture_data_url']))
        self.assertEqual(
            utils.get_time_in_millisecs(first_suggestion.created_on),
            messages_summary['created_on_msecs'])
        self.assertEqual(
            messages_summary['suggestion_html'], '<p>new content html</p>')
        self.assertEqual(
            messages_summary['current_content_html'], current_content_html)
        self.assertEqual(
            messages_summary['description'], suggestion_thread.subject)
        self.logout()
Beispiel #6
0
    def reduce(thread_id, stringified_message_ids):
        message_ids = [
            ast.literal_eval(v) for v in stringified_message_ids]

        thread_model = feedback_models.FeedbackThreadModel.get(thread_id)
        next_message_id = max(message_ids) + 1
        thread_model.message_count = next_message_id
        thread_model.put(update_last_updated_time=False)

        if next_message_id != len(message_ids):
            thread = feedback_services.get_thread(thread_id)
            logging.error(
                'The number of messages in the thread, given by the id %s is %s'
                '. But the number of messages as estimated by the message ids '
                'is %s. Therefore the estimate is not equal to the actual '
                'number of messages.' % (
                    thread_id, len(message_ids), next_message_id))

            yield (
                'error', {
                    'subject': thread.subject,
                    'thread_id': thread_id,
                    'next_message_id': next_message_id,
                    'message_count': len(message_ids)
                })
Beispiel #7
0
    def get(self, thread_id):
        """Handles GET requests."""
        messages = feedback_services.get_messages(thread_id)
        author_ids = [m.author_id for m in messages]
        authors_settings = user_services.get_users_settings(author_ids)

        message_ids = [m.message_id for m in messages]
        feedback_services.update_messages_read_by_the_user(
            self.user_id, thread_id, message_ids)

        message_summary_list = []
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        suggestion_thread = feedback_services.get_thread(thread_id)

        exploration_id = feedback_services.get_exp_id_from_thread_id(thread_id)
        if suggestion:
            exploration = exp_fetchers.get_exploration_by_id(exploration_id)
            current_content_html = (
                exploration.states[suggestion.change.state_name].content.html)
            suggestion_summary = {
                'suggestion_html':
                suggestion.change.new_value['html'],
                'current_content_html':
                current_content_html,
                'description':
                suggestion_thread.subject,
                'author_username':
                authors_settings[0].username,
                'author_picture_data_url':
                (authors_settings[0].profile_picture_data_url),
                'created_on_msecs':
                utils.get_time_in_millisecs(messages[0].created_on)
            }
            message_summary_list.append(suggestion_summary)
            messages.pop(0)
            authors_settings.pop(0)

        for m, author_settings in python_utils.ZIP(messages, authors_settings):

            if author_settings is None:
                author_username = None
                author_picture_data_url = None
            else:
                author_username = author_settings.username
                author_picture_data_url = (
                    author_settings.profile_picture_data_url)

            message_summary = {
                'message_id': m.message_id,
                'text': m.text,
                'updated_status': m.updated_status,
                'author_username': author_username,
                'author_picture_data_url': author_picture_data_url,
                'created_on_msecs': utils.get_time_in_millisecs(m.created_on)
            }
            message_summary_list.append(message_summary)

        self.render_json({'message_summary_list': message_summary_list})
Beispiel #8
0
    def post(self):
        payload = json.loads(self.request.body)
        user_id = payload['user_id']
        reference_dict = payload['reference_dict']

        message = feedback_services.get_message(
            reference_dict['exploration_id'], reference_dict['thread_id'],
            reference_dict['message_id'])
        exploration = exp_services.get_exploration_by_id(
            reference_dict['exploration_id'])
        thread = feedback_services.get_thread(
            reference_dict['exploration_id'], reference_dict['thread_id'])

        subject = 'New Oppia message in "%s"' % thread.subject
        email_manager.send_instant_feedback_message_email(
            user_id, message.author_id, message.text, subject,
            exploration.title, reference_dict['exploration_id'], thread.subject)
Beispiel #9
0
    def post(self):
        payload = json.loads(self.request.body)
        user_id = payload['user_id']
        reference_dict = payload['reference_dict']
        old_status = payload['old_status']
        new_status = payload['new_status']

        message = feedback_services.get_message(
            reference_dict['thread_id'], reference_dict['message_id'])
        exploration = exp_fetchers.get_exploration_by_id(
            reference_dict['entity_id'])
        thread = feedback_services.get_thread(reference_dict['thread_id'])

        text = 'changed status from %s to %s' % (old_status, new_status)
        subject = 'Oppia thread status change: "%s"' % thread.subject
        email_manager.send_instant_feedback_message_email(
            user_id, message.author_id, text, subject, exploration.title,
            reference_dict['entity_id'], thread.subject)
Beispiel #10
0
    def post(self):
        payload = json.loads(self.request.body)
        user_id = payload['user_id']
        reference_dict = payload['reference_dict']
        old_status = payload['old_status']
        new_status = payload['new_status']

        message = feedback_services.get_message(
            reference_dict['exploration_id'], reference_dict['thread_id'],
            reference_dict['message_id'])
        exploration = exp_services.get_exploration_by_id(
            reference_dict['exploration_id'])
        thread = feedback_services.get_thread(
            reference_dict['exploration_id'], reference_dict['thread_id'])

        text = 'changed status from %s to %s' % (old_status, new_status)
        subject = 'Oppia thread status change: "%s"' % thread.subject
        email_manager.send_instant_feedback_message_email(
            user_id, message.author_id, text, subject, exploration.title,
            reference_dict['exploration_id'], thread.subject)
    def map(item):
        """Implements the map function (generator). Computes most recent
        activity commits and feedbacks of a specific user.

        Args:
            item: UserSubscriptionsModel. An instance of UserSubscriptionsModel.

        Yields:
            tuple(key, recent_activity_commits), where:
                key: str. Uses the form 'user_id@job_queued_msec'.
                recent_activity_commits: dict. Has the keys:
                    type: str. Either feconf.UPDATE_TYPE_EXPLORATION_COMMIT or
                        feconf.UPDATE_TYPE_FEEDBACK_MESSAGE.
                    activity_id: str. The id of the exploration being committed
                        to or to which the feedback thread belongs.
                    activity_title: str. The title of the activity.
                    last_updated_ms: float. The time when the update was made,
                        in milliseconds since the Epoch.
                    author_id: str. The id of the author who made the update.
                    subject: str. A brief description of the recent updates.
        """
        user_id = item.id
        job_queued_msec = RecentUpdatesMRJobManager._get_job_queued_msec()
        reducer_key = '%s@%s' % (user_id, job_queued_msec)

        exploration_ids_list = item.activity_ids
        collection_ids_list = item.collection_ids
        feedback_thread_ids_list = item.general_feedback_thread_ids

        (most_recent_activity_commits, tracked_exp_models_for_feedback) = (
            RecentUpdatesMRJobManager._get_most_recent_activity_commits(
                exp_models.ExplorationModel, exploration_ids_list,
                'exploration', feconf.UPDATE_TYPE_EXPLORATION_COMMIT,
                feconf.COMMIT_MESSAGE_EXPLORATION_DELETED))

        for exp_model in tracked_exp_models_for_feedback:
            threads = feedback_services.get_all_threads(
                feconf.ENTITY_TYPE_EXPLORATION, exp_model.id, False)
            for thread in threads:
                if thread.id not in feedback_thread_ids_list:
                    feedback_thread_ids_list.append(thread.id)

        # TODO(bhenning): Implement a solution to having feedback threads for
        # collections.
        most_recent_activity_commits += (
            RecentUpdatesMRJobManager._get_most_recent_activity_commits(
                collection_models.CollectionModel, collection_ids_list,
                'collection', feconf.UPDATE_TYPE_COLLECTION_COMMIT,
                feconf.COMMIT_MESSAGE_COLLECTION_DELETED))[0]

        for recent_activity_commit_dict in most_recent_activity_commits:
            yield (reducer_key, recent_activity_commit_dict)

        for feedback_thread_id in feedback_thread_ids_list:
            last_message = (feedback_models.GeneralFeedbackMessageModel.
                            get_most_recent_message(feedback_thread_id))
            exploration_id = last_message.entity_id

            yield (reducer_key, {
                'type':
                feconf.UPDATE_TYPE_FEEDBACK_MESSAGE,
                'activity_id':
                exploration_id,
                'activity_title':
                exp_models.ExplorationModel.get_by_id(exploration_id).title,
                'author_id':
                last_message.author_id,
                'last_updated_ms':
                utils.get_time_in_millisecs(last_message.created_on),
                'subject':
                feedback_services.get_thread(last_message.thread_id).subject
            })
Beispiel #12
0
    def test_get_thread_summaries(self):
        feedback_services.create_thread(
            'exploration', self.EXP_ID_1, self.user_id,
            self.EXPECTED_THREAD_DICT['subject'], 'not used here')
        feedback_services.create_thread(
            'exploration', self.EXP_ID_2, self.user_id,
            self.EXPECTED_THREAD_DICT['subject'], 'not used here')

        # The message count parameter is missing for this thread. The thread
        # summaries function should account for this and function
        # flawlessly.
        thread_3 = feedback_models.GeneralFeedbackThreadModel(
            id='exploration.' + self.EXP_ID_3 + '.' + self.THREAD_ID,
            entity_type='exploration', entity_id=self.EXP_ID_3,
            original_author_id=self.user_id, subject='Feedback',
            status=feedback_models.STATUS_CHOICES_OPEN, has_suggestion=False)
        thread_3.put()
        feedback_services.create_message(
            'exploration.' + self.EXP_ID_3 + '.' + self.THREAD_ID,
            self.user_id, None, None, 'not used here')


        thread_ids = subscription_services.get_all_threads_subscribed_to(
            self.user_id)
        thread_ids.append('exploration.' + self.EXP_ID_3 + '.' + self.THREAD_ID)
        thread_summaries, number_of_unread_threads = (
            feedback_services.get_thread_summaries(
                self.user_id, thread_ids))
        exploration_titles = (
            ['Bridges in England', 'Sillat Suomi', 'Leaning tower of Pisa'])

        # Fetch the threads.
        threads = []
        threads.append(feedback_services.get_thread(thread_ids[0]))
        threads.append(feedback_services.get_thread(thread_ids[1]))
        threads.append(feedback_services.get_thread(
            'exploration.' + self.EXP_ID_3 + '.' + self.THREAD_ID))
        # Check if the number of unread messages match.
        self.assertEqual(number_of_unread_threads, 0)
        for index, thread in enumerate(threads):
            thread_summary = {
                'status': thread.status,
                'original_author_id': thread.original_author_id,
                'last_updated': thread_summaries[index]['last_updated'],
                'last_message_text': 'not used here',
                'total_message_count': 1,
                'last_message_read': True,
                'second_last_message_read': None,
                'author_last_message': user_services.get_username(
                    self.user_id),
                'author_second_last_message': None,
                'exploration_title': exploration_titles[index]
            }
            # Check if the summaries match.
            self.assertDictContainsSubset(
                thread_summary, thread_summaries[index])

        feedback_services.create_message(
            threads[0].id, self.owner_id, None, None, 'editor message')
        _, number_of_unread_threads = (
            feedback_services.get_thread_summaries(self.user_id, thread_ids))

        # Check if the number of unread messages is equal to 1.
        self.assertEqual(number_of_unread_threads, 1)