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()
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({})
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({})
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()
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) })
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})
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)
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)
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 })
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)