def test_get_by_reviewer(self): queries = [('final_reviewer_id', self.reviewer_id_1)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 1) queries = [('final_reviewer_id', self.reviewer_id_2)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 2)
def test_get_by_author(self): queries = [('author_id', self.author_id_1)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 3) queries = [('author_id', self.author_id_2)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 2)
def test_get_by_target_id(self): queries = [('target_type', suggestion_models.TARGET_TYPE_EXPLORATION), ('target_id', self.target_id_1)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 4) queries = [('target_type', suggestion_models.TARGET_TYPE_EXPLORATION), ('target_id', self.target_id_2)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 1)
def test_accept_suggestion_and_send_email_to_author(self): enable_recording_of_scores_swap = self.swap( feconf, 'ENABLE_RECORDING_OF_SCORES', True) send_suggestion_review_related_emails_swap = self.swap( feconf, 'SEND_SUGGESTION_REVIEW_RELATED_EMAILS', True) change_list = [ exp_domain.ExplorationChange({ 'cmd': exp_domain.CMD_ADD_STATE, 'state_name': 'state 1', }) ] exp_services.update_exploration(self.author_id, self.target_id, change_list, 'Add state.') new_suggestion_content = state_domain.SubtitledHtml( 'content', 'new suggestion content html').to_dict() change_dict = { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'state 1', 'new_value': new_suggestion_content } suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, change_dict, 'test description', self.reviewer_id) suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.target_id) ])[0] self.assertEqual(suggestion.status, suggestion_models.STATUS_IN_REVIEW) self.assertFalse( suggestion_services.check_if_email_has_been_sent_to_user( self.author_id, suggestion.score_category)) suggestion_services.increment_score_for_user(self.author_id, suggestion.score_category, 10) with enable_recording_of_scores_swap, ( send_suggestion_review_related_emails_swap): suggestion_services.accept_suggestion(suggestion, self.reviewer_id, self.COMMIT_MESSAGE, 'review message') suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.target_id) ])[0] self.assertEqual(suggestion.status, suggestion_models.STATUS_ACCEPTED) self.assertTrue( suggestion_services.check_if_email_has_been_sent_to_user( self.author_id, suggestion.score_category))
def test_post_feedback_threads_with_updated_suggestion_status_raises_400( self): self.login(self.OWNER_EMAIL_1) csrf_token = self.get_new_csrf_token() new_content = state_domain.SubtitledHtml( 'content', '<p>new content html</p>').to_dict() change = { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'Welcome!', 'new_value': new_content } suggestion_services.create_suggestion( feconf.SUGGESTION_TYPE_EDIT_STATE_CONTENT, feconf.ENTITY_TYPE_EXPLORATION, self.EXP_ID, 1, self.owner_id_1, change, 'sample description') thread_id = suggestion_services.query_suggestions( [('author_id', self.owner_id_1), ('target_id', self.EXP_ID)])[0].suggestion_id thread_url = '%s/%s' % (feconf.FEEDBACK_THREAD_URL_PREFIX, thread_id) response = self.post_json( thread_url, { 'text': 'Message 1', 'updated_subject': None, 'updated_status': 'open' }, csrf_token=csrf_token, expected_status_int=400) self.assertEqual( response['error'], 'Suggestion thread status cannot be changed manually.') self.logout()
def test_resubmit_rejected_suggestion(self): self.login(self.EDITOR_EMAIL) response = self.testapp.get('/explore/%s' % self.EXP_ID) csrf_token = self.get_csrf_token_from_response(response) suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.EXP_ID) ])[0] suggestion_services.reject_suggestion(suggestion, self.reviewer_id, 'reject message') self.logout() self.login(self.AUTHOR_EMAIL) response = self.testapp.get('/explore/%s' % self.EXP_ID) csrf_token = self.get_csrf_token_from_response(response) self.put_json( '%s/resubmit/%s' % (feconf.SUGGESTION_ACTION_URL_PREFIX, suggestion.suggestion_id), { 'summary_message': 'summary message', 'action': u'resubmit', 'change': { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'State 1', 'new_value': self.resubmit_change_content, 'old_value': self.old_content } }, csrf_token=csrf_token) suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.EXP_ID) ])[0] self.assertEqual(suggestion.status, suggestion_models.STATUS_IN_REVIEW) self.assertEqual(suggestion.change.new_value['html'], self.resubmit_change_content['html']) self.assertEqual(suggestion.change.cmd, exp_domain.CMD_EDIT_STATE_PROPERTY) self.assertEqual(suggestion.change.property_name, exp_domain.STATE_PROPERTY_CONTENT) self.assertEqual(suggestion.change.state_name, 'State 1') self.logout()
def test_query_suggestions(self): queries = [('target_type', suggestion_models.TARGET_TYPE_EXPLORATION), ('target_id', self.target_id_1), ('author_id', self.author_id_2)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 1) queries = [('target_type', suggestion_models.TARGET_TYPE_EXPLORATION), ('target_id', self.target_id_1), ('author_id', self.author_id_1), ('status', suggestion_models.STATUS_IN_REVIEW)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 3) queries = [('target_type', suggestion_models.TARGET_TYPE_EXPLORATION), ('target_id', self.target_id_1), ('invalid_field', 'value')] with self.assertRaisesRegexp( Exception, 'Not allowed to query on field invalid_field'): suggestion_services.query_suggestions(queries)
def get(self): """Handles get requests.""" if feconf.SEND_SUGGESTION_REVIEW_RELATED_EMAILS: score_categories = suggestion_models.get_all_score_categories() for score_category in score_categories: suggestions = suggestion_services.query_suggestions( [('score_category', score_category), ('status', suggestion_models.STATUS_ACCEPTED)]) if len(suggestions) > 0: reviewer_id = suggestion_services.get_next_user_in_rotation( score_category) email_manager.send_mail_to_notify_users_to_review( reviewer_id, score_category)
def test_email_is_not_sent_to_unregistered_user(self): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, self.change, 'test description', self.reviewer_id) suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.target_id) ])[0] self.assertFalse( suggestion_services.check_if_email_has_been_sent_to_user( 'unregistered_user_id', suggestion.score_category))
def test_cannot_reject_suggestion_with_empty_review_message(self): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, self.change, 'test description', self.reviewer_id) suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.target_id) ])[0] with self.assertRaisesRegexp(Exception, 'Review message cannot be empty.'): suggestion_services.reject_suggestion(suggestion, self.reviewer_id, '')
def test_cannot_mark_review_completed_with_invalid_status(self): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, self.change, 'test description', self.reviewer_id) suggestion = suggestion_services.query_suggestions( [('author_id', self.author_id), ( 'target_id', self.target_id)])[0] with self.assertRaisesRegexp(Exception, 'Invalid status after review.'): suggestion_services.mark_review_completed( suggestion, 'invalid_status', self.reviewer_id)
def test_cannot_mark_email_has_been_sent_to_user_with_no_user_scoring_model( self): suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, self.target_id, self.target_version_at_submission, self.author_id, self.change, 'test description', self.reviewer_id) suggestion = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', self.target_id) ])[0] with self.assertRaisesRegexp( Exception, 'Expected user scoring model to exist for user'): suggestion_services.mark_email_has_been_sent_to_user( 'unregistered_user_id', suggestion.score_category)
def test_suggestion_to_exploration_handler_with_invalid_target_type(self): self.login(self.EDITOR_EMAIL) question_dict = { 'question_state_data': self._create_valid_question_data('default_state').to_dict(), 'language_code': 'en', 'question_state_data_schema_version': (feconf.CURRENT_STATE_SCHEMA_VERSION) } exp_id = 'new_exp_id' self.save_new_default_exploration(exp_id, self.editor_id) suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_ADD_QUESTION, suggestion_models.TARGET_TYPE_TOPIC, exp_id, 1, self.author_id, { 'cmd': (question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION), 'question_dict': question_dict, 'skill_id': None }, None, None) suggestion_id = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', exp_id) ])[0].suggestion_id response = self.get_html_response('/explore/%s' % exp_id) csrf_token = self.get_csrf_token_from_response(response) response = self.put_json( '%s/exploration/%s/%s' % (feconf.SUGGESTION_ACTION_URL_PREFIX, exp_id, suggestion_id), { 'action': u'reject', 'review_message': u'Rejected!' }, csrf_token=csrf_token, expected_status_int=400) self.assertEqual( response['error'], 'This handler allows actions only on suggestions to explorations.') self.logout()
def get(self): # The query_fields_and_values variable is a list of tuples. The first # element in each tuple is the field being queried and the second # element is the value of the field being queried. # request.GET.items() parses the params from the url into the above # format. So in the url, the query should be passed as: # ?field1=value1&field2=value2...fieldN=valueN. query_fields_and_values = self.request.GET.items() for query in query_fields_and_values: if query[0] not in suggestion_models.ALLOWED_QUERY_FIELDS: raise Exception('Not allowed to query on field %s' % query[0]) suggestions = suggestion_services.query_suggestions( query_fields_and_values) self.values.update({'suggestions': [s.to_dict() for s in suggestions]}) self.render_json(self.values)
def test_suggestion_to_topic_handler_with_invalid_target_type(self): self.login(self.ADMIN_EMAIL) exp_id = 'new_exp_id' self.save_new_default_exploration(exp_id, self.admin_id) new_content = state_domain.SubtitledHtml('content', 'new content html').to_dict() change_cmd = { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'State 1', 'new_value': new_content } suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, exp_id, 1, self.author_id, change_cmd, 'sample description', None) suggestion_id = suggestion_services.query_suggestions([ ('author_id', self.author_id), ('target_id', exp_id) ])[0].suggestion_id response = self.get_html_response(feconf.CREATOR_DASHBOARD_URL) csrf_token = self.get_csrf_token_from_response(response) with self.swap(constants, 'ENABLE_NEW_STRUCTURE_PLAYERS', True): response = self.put_json('%s/topic/%s/%s' % (feconf.SUGGESTION_ACTION_URL_PREFIX, self.topic_id, suggestion_id), { 'action': u'reject', 'review_message': u'Rejected!' }, csrf_token=csrf_token, expected_status_int=400) self.assertEqual( response['error'], 'This handler allows actions only on suggestions to topics.') self.logout()
def test_owner_of_exploration_cannot_repond_to_own_suggestion(self): self.login(self.EDITOR_EMAIL) exp_id = 'new_exp_id' self.save_new_default_exploration(exp_id, self.editor_id) new_content = state_domain.SubtitledHtml('content', 'new content html').to_dict() change_cmd = { 'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY, 'property_name': exp_domain.STATE_PROPERTY_CONTENT, 'state_name': 'State 1', 'new_value': new_content } suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, exp_id, 1, self.editor_id, change_cmd, 'sample description', None) suggestion_id = suggestion_services.query_suggestions([ ('author_id', self.editor_id), ('target_id', exp_id) ])[0].suggestion_id response = self.get_html_response('/explore/%s' % exp_id) csrf_token = self.get_csrf_token_from_response(response) response = self.put_json( '%s/exploration/%s/%s' % (feconf.SUGGESTION_ACTION_URL_PREFIX, exp_id, suggestion_id), { 'action': u'reject', 'review_message': u'Rejected!' }, csrf_token=csrf_token, expected_status_int=401) self.assertEqual(response['error'], 'You cannot accept/reject your own suggestion.') self.logout()
def get(self): """Handles GET requests.""" def _round_average_ratings(rating): """Returns the rounded average rating to display on the creator dashboard. Args: rating: float. The rating of the lesson. Returns: float. The rounded average value of rating. """ return python_utils.ROUND( rating, feconf.AVERAGE_RATINGS_DASHBOARD_PRECISION) subscribed_exploration_summaries = ( exp_fetchers.get_exploration_summaries_subscribed_to(self.user_id)) subscribed_collection_summaries = ( collection_services.get_collection_summaries_subscribed_to( self.user_id)) exploration_ids_subscribed_to = [ summary.id for summary in subscribed_exploration_summaries ] exp_summary_dicts = summary_services.get_displayable_exp_summary_dicts( subscribed_exploration_summaries) collection_summary_dicts = [] feedback_thread_analytics = ( feedback_services.get_thread_analytics_multi( exploration_ids_subscribed_to)) # TODO(bhenning): Update this to use unresolved answers from # stats_services once the training interface is enabled and it's cheaper # to retrieve top answers from stats_services. for ind, exploration in enumerate(exp_summary_dicts): exploration.update(feedback_thread_analytics[ind].to_dict()) exp_summary_dicts = sorted( exp_summary_dicts, key=lambda x: (x['num_open_threads'], x['last_updated_msec']), reverse=True) if constants.ENABLE_NEW_STRUCTURE_PLAYERS: topic_summaries = topic_services.get_all_topic_summaries() topic_summary_dicts = [ summary.to_dict() for summary in topic_summaries ] if role_services.ACTION_CREATE_COLLECTION in self.user.actions: for collection_summary in subscribed_collection_summaries: # TODO(sll): Reuse _get_displayable_collection_summary_dicts() # in summary_services, instead of replicating it like this. collection_summary_dicts.append({ 'id': collection_summary.id, 'title': collection_summary.title, 'category': collection_summary.category, 'objective': collection_summary.objective, 'language_code': collection_summary.language_code, 'last_updated_msec': utils.get_time_in_millisecs( collection_summary.collection_model_last_updated), 'created_on': utils.get_time_in_millisecs( collection_summary.collection_model_created_on), 'status': collection_summary.status, 'node_count': collection_summary.node_count, 'community_owned': collection_summary.community_owned, 'thumbnail_icon_url': (utils.get_thumbnail_icon_url_for_category( collection_summary.category)), 'thumbnail_bg_color': utils.get_hex_color_for_category( collection_summary.category), }) dashboard_stats = ( user_jobs_continuous.UserStatsAggregator.get_dashboard_stats( self.user_id)) dashboard_stats.update({ 'total_open_feedback': feedback_services.get_total_open_threads(feedback_thread_analytics) }) if dashboard_stats and dashboard_stats.get('average_ratings'): dashboard_stats['average_ratings'] = (_round_average_ratings( dashboard_stats['average_ratings'])) last_week_stats = (user_services.get_last_week_dashboard_stats( self.user_id)) if last_week_stats and len(list(last_week_stats.keys())) != 1: logging.error( '\'last_week_stats\' should contain only one key-value pair' ' denoting last week dashboard stats of the user keyed by a' ' datetime string.') last_week_stats = None if last_week_stats: # 'last_week_stats' is a dict with only one key-value pair denoting # last week dashboard stats of the user keyed by a datetime string. datetime_of_stats = list(last_week_stats.keys())[0] last_week_stats_average_ratings = (list( last_week_stats.values())[0].get('average_ratings')) if last_week_stats_average_ratings: last_week_stats[datetime_of_stats]['average_ratings'] = ( _round_average_ratings(last_week_stats_average_ratings)) subscriber_ids = subscription_services.get_all_subscribers_of_creator( self.user_id) subscribers_settings = user_services.get_users_settings(subscriber_ids) subscribers_list = [] for index, subscriber_settings in enumerate(subscribers_settings): subscriber_summary = { 'subscriber_picture_data_url': (subscriber_settings.profile_picture_data_url), 'subscriber_username': subscriber_settings.username, 'subscriber_impact': (user_services.get_user_impact_score(subscriber_ids[index])) } subscribers_list.append(subscriber_summary) user_settings = user_services.get_user_settings(self.user_id, strict=False) creator_dashboard_display_pref = ( user_settings.creator_dashboard_display_pref) suggestions_created_by_user = suggestion_services.query_suggestions([ ('author_id', self.user_id), ('suggestion_type', suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT) ]) suggestions_which_can_be_reviewed = ( suggestion_services. get_all_suggestions_that_can_be_reviewed_by_user(self.user_id)) for s in suggestions_created_by_user: s.populate_old_value_of_change() for s in suggestions_which_can_be_reviewed: s.populate_old_value_of_change() suggestion_dicts_created_by_user = ([ s.to_dict() for s in suggestions_created_by_user ]) suggestion_dicts_which_can_be_reviewed = ([ s.to_dict() for s in suggestions_which_can_be_reviewed ]) ids_of_suggestions_created_by_user = ([ s['suggestion_id'] for s in suggestion_dicts_created_by_user ]) ids_of_suggestions_which_can_be_reviewed = ([ s['suggestion_id'] for s in suggestion_dicts_which_can_be_reviewed ]) threads_linked_to_suggestions_by_user = ([ t.to_dict() for t in feedback_services.get_multiple_threads( ids_of_suggestions_created_by_user) ]) threads_linked_to_suggestions_which_can_be_reviewed = ([ t.to_dict() for t in feedback_services.get_multiple_threads( ids_of_suggestions_which_can_be_reviewed) ]) self.values.update({ 'explorations_list': exp_summary_dicts, 'collections_list': collection_summary_dicts, 'dashboard_stats': dashboard_stats, 'last_week_stats': last_week_stats, 'subscribers_list': subscribers_list, 'display_preference': creator_dashboard_display_pref, 'threads_for_created_suggestions_list': (threads_linked_to_suggestions_by_user), 'threads_for_suggestions_to_review_list': (threads_linked_to_suggestions_which_can_be_reviewed), 'created_suggestions_list': suggestion_dicts_created_by_user, 'suggestions_to_review_list': suggestion_dicts_which_can_be_reviewed }) if constants.ENABLE_NEW_STRUCTURE_PLAYERS: self.values.update({'topic_summary_dicts': topic_summary_dicts}) self.render_json(self.values)
def test_get_by_type(self): queries = [('suggestion_type', suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 5)
def test_get_by_status(self): queries = [('status', suggestion_models.STATUS_IN_REVIEW)] self.assertEqual(len(suggestion_services.query_suggestions(queries)), 5)
def get(self): """Handles GET requests.""" def _get_intro_card_color(category): return ( constants.CATEGORIES_TO_COLORS[category] if category in constants.CATEGORIES_TO_COLORS else constants.DEFAULT_COLOR) def _round_average_ratings(rating): return round(rating, feconf.AVERAGE_RATINGS_DASHBOARD_PRECISION) # We need to do the filtering because some activities that were # originally subscribed to may have been deleted since. subscribed_exploration_summaries = [ summary for summary in exp_services.get_exploration_summaries_matching_ids( subscription_services.get_exploration_ids_subscribed_to( self.user_id)) if summary is not None] subscribed_collection_summaries = [ summary for summary in collection_services.get_collection_summaries_matching_ids( subscription_services.get_collection_ids_subscribed_to( self.user_id)) if summary is not None] exploration_ids_subscribed_to = [ summary.id for summary in subscribed_exploration_summaries] exp_summary_dicts = summary_services.get_displayable_exp_summary_dicts( subscribed_exploration_summaries) collection_summary_dicts = [] feedback_thread_analytics = ( feedback_services.get_thread_analytics_multi( exploration_ids_subscribed_to)) # TODO(bhenning): Update this to use unresolved answers from # stats_services once the training interface is enabled and it's cheaper # to retrieve top answers from stats_services. for ind, exploration in enumerate(exp_summary_dicts): exploration.update(feedback_thread_analytics[ind].to_dict()) exp_summary_dicts = sorted( exp_summary_dicts, key=lambda x: (x['num_open_threads'], x['last_updated_msec']), reverse=True) if constants.ENABLE_NEW_STRUCTURE_PLAYERS: topic_summaries = topic_services.get_all_topic_summaries() topic_summary_dicts = [ summary.to_dict() for summary in topic_summaries] if role_services.ACTION_CREATE_COLLECTION in self.user.actions: for collection_summary in subscribed_collection_summaries: # TODO(sll): Reuse _get_displayable_collection_summary_dicts() # in summary_services, instead of replicating it like this. collection_summary_dicts.append({ 'id': collection_summary.id, 'title': collection_summary.title, 'category': collection_summary.category, 'objective': collection_summary.objective, 'language_code': collection_summary.language_code, 'last_updated': utils.get_time_in_millisecs( collection_summary.collection_model_last_updated), 'created_on': utils.get_time_in_millisecs( collection_summary.collection_model_created_on), 'status': collection_summary.status, 'node_count': collection_summary.node_count, 'community_owned': collection_summary.community_owned, 'thumbnail_icon_url': ( utils.get_thumbnail_icon_url_for_category( collection_summary.category)), 'thumbnail_bg_color': utils.get_hex_color_for_category( collection_summary.category), }) dashboard_stats = ( user_jobs_continuous.UserStatsAggregator.get_dashboard_stats( self.user_id)) dashboard_stats.update({ 'total_open_feedback': feedback_services.get_total_open_threads( feedback_thread_analytics) }) if dashboard_stats and dashboard_stats.get('average_ratings'): dashboard_stats['average_ratings'] = ( _round_average_ratings(dashboard_stats['average_ratings'])) last_week_stats = ( user_services.get_last_week_dashboard_stats(self.user_id)) if last_week_stats and last_week_stats.get('average_ratings'): last_week_stats['average_ratings'] = ( _round_average_ratings(last_week_stats['average_ratings'])) subscriber_ids = subscription_services.get_all_subscribers_of_creator( self.user_id) subscribers_settings = user_services.get_users_settings(subscriber_ids) subscribers_list = [] for index, subscriber_settings in enumerate(subscribers_settings): subscriber_summary = { 'subscriber_picture_data_url': ( subscriber_settings.profile_picture_data_url), 'subscriber_username': subscriber_settings.username, 'subscriber_impact': ( user_services.get_user_impact_score(subscriber_ids[index])) } subscribers_list.append(subscriber_summary) user_settings = user_services.get_user_settings( self.user_id, strict=False) creator_dashboard_display_pref = ( user_settings.creator_dashboard_display_pref) suggestions_created_by_user = suggestion_services.query_suggestions( [('author_id', self.user_id), ( 'suggestion_type', suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT)]) suggestions_which_can_be_reviewed = ( suggestion_services .get_all_suggestions_that_can_be_reviewed_by_user(self.user_id)) for s in suggestions_created_by_user: s.populate_old_value_of_change() for s in suggestions_which_can_be_reviewed: s.populate_old_value_of_change() suggestion_dicts_created_by_user = ( [s.to_dict() for s in suggestions_created_by_user]) suggestion_dicts_which_can_be_reviewed = ( [s.to_dict() for s in suggestions_which_can_be_reviewed]) ids_of_suggestions_created_by_user = ( [s['suggestion_id'] for s in suggestion_dicts_created_by_user]) ids_of_suggestions_which_can_be_reviewed = ( [s['suggestion_id'] for s in suggestion_dicts_which_can_be_reviewed]) threads_linked_to_suggestions_by_user = ( [t.to_dict() for t in feedback_services.get_multiple_threads( ids_of_suggestions_created_by_user)]) threads_linked_to_suggestions_which_can_be_reviewed = ( [t.to_dict() for t in feedback_services.get_multiple_threads( ids_of_suggestions_which_can_be_reviewed)]) self.values.update({ 'explorations_list': exp_summary_dicts, 'collections_list': collection_summary_dicts, 'dashboard_stats': dashboard_stats, 'last_week_stats': last_week_stats, 'subscribers_list': subscribers_list, 'display_preference': creator_dashboard_display_pref, 'threads_for_created_suggestions_list': ( threads_linked_to_suggestions_by_user), 'threads_for_suggestions_to_review_list': ( threads_linked_to_suggestions_which_can_be_reviewed), 'created_suggestions_list': suggestion_dicts_created_by_user, 'suggestions_to_review_list': suggestion_dicts_which_can_be_reviewed }) if constants.ENABLE_NEW_STRUCTURE_PLAYERS: self.values.update({ 'topic_summary_dicts': topic_summary_dicts }) self.render_json(self.values)
def test_cron_mail_reviewers_in_rotation_handler(self): self.login(self.ADMIN_EMAIL, is_super_admin=True) reviewer_ids = [] score_categories = [] def _mock_send_mail_to_notify_users_to_review(reviewer_id, score_category): """Mocks email_manager.send_mail_to_notify_users_to_review() as its not possible to send mail with self.testapp_swap, i.e with the URLs defined in main_cron. """ reviewer_ids.append(reviewer_id) score_categories.append(score_category) send_mail_to_notify_users_to_review_swap = self.swap( email_manager, 'send_mail_to_notify_users_to_review', _mock_send_mail_to_notify_users_to_review) self.save_new_valid_exploration('exp_id', self.admin_id, title='A title', category='Algebra') new_content = state_domain.SubtitledHtml( 'content', '<p>new suggestion content</p>').to_dict() change = { 'cmd': 'edit_state_property', 'property_name': 'content', 'state_name': 'Introduction', 'new_value': new_content } suggestion_services.create_suggestion( suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT, suggestion_models.TARGET_TYPE_EXPLORATION, 'exp_id', 1, feconf.SYSTEM_COMMITTER_ID, change, 'change title', self.admin_id) exploration = exp_services.get_exploration_by_id('exp_id') self.assertEqual(exploration.states['Introduction'].content.to_dict(), { 'content_id': 'content', 'html': '' }) suggestion = suggestion_services.query_suggestions([ ('author_id', feconf.SYSTEM_COMMITTER_ID), ('target_id', 'exp_id') ])[0] suggestion_services.accept_suggestion( suggestion, self.admin_id, suggestion_models.DEFAULT_SUGGESTION_ACCEPT_MESSAGE, None) exploration = exp_services.get_exploration_by_id('exp_id') self.assertEqual(exploration.states['Introduction'].content.to_dict(), { 'content_id': 'content', 'html': '<p>new suggestion content</p>' }) send_suggestion_review_related_emails_swap = self.swap( feconf, 'SEND_SUGGESTION_REVIEW_RELATED_EMAILS', True) with self.testapp_swap, send_suggestion_review_related_emails_swap, ( send_mail_to_notify_users_to_review_swap): self.get_html_response('/cron/suggestions/notify_reviewers') self.assertEqual(reviewer_ids, [None]) self.assertEqual(score_categories, ['content.Algebra'])