Пример #1
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(messages_summary['author_picture_data_url'].startswith(
            'data:image/png;'))
        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',
                                                 'new content html').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(
            suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
            suggestion_models.TARGET_TYPE_EXPLORATION, self.EXP_ID_1, 1,
            suggestion_models.STATUS_IN_REVIEW, self.editor_id, None,
            change_cmd, 'score category', thread_id)

        suggestion_thread = feedback_services.get_thread(thread_id)
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        exploration = exp_services.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]

        self.assertEqual(messages_summary['author_username'],
                         self.EDITOR_USERNAME)
        self.assertTrue(messages_summary['author_picture_data_url'].startswith(
            'data:image/png;'))
        self.assertEqual(messages_summary['suggestion_html'],
                         'new content html')
        self.assertEqual(messages_summary['current_content_html'],
                         current_content_html)
        self.assertEqual(messages_summary['description'],
                         suggestion_thread.subject)
        self.logout()
Пример #2
0
    def test_reject_suggestion_successfully(self):
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            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_cmd, self.score_category, 'test description',
                self.assigned_reviewer_id, self.reviewer_id)
        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)

        suggestion_services.reject_suggestion(suggestion, self.reviewer_id,
                                              'reject review message')
        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)
        self.assertEqual(suggestion.status, suggestion_models.STATUS_REJECTED)
        self.assertEqual(suggestion.final_reviewer_id, self.reviewer_id)
        thread_messages = feedback_services.get_messages(self.THREAD_ID)
        last_message = thread_messages[len(thread_messages) - 1]
        self.assertEqual(last_message.text, 'reject review message')
Пример #3
0
    def post(self):
        payload = json.loads(self.request.body)
        exploration_id = payload['exploration_id']
        thread_id = payload['thread_id']

        exploration_rights = (
            rights_manager.get_exploration_rights(exploration_id))
        exploration = exp_fetchers.get_exploration_by_id(exploration_id)
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)

        email_manager.send_suggestion_email(
            exploration.title, exploration.id, suggestion.author_id,
            exploration_rights.owner_ids)
Пример #4
0
 def put(self, suggestion_id):
     suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)
     if not suggestion:
         raise self.InvalidInputException(
             'No suggestion found with given suggestion id')
     new_change = self.payload.get('change')
     change_cls = type(suggestion.change)
     change_object = change_cls(new_change)
     suggestion.pre_update_validate(change_object)
     suggestion.change = change_object
     summary_message = self.payload.get('summary_message')
     suggestion_services.resubmit_rejected_suggestion(
         suggestion, summary_message, self.user_id)
     self.render_json(self.values)
Пример #5
0
 def test_resubmit_rejected_suggestion_success(self):
     with self.swap(
         feedback_models.GeneralFeedbackThreadModel,
         'generate_new_thread_id', self.mock_generate_new_thread_id):
         with self.swap(
             exp_fetchers, 'get_exploration_by_id',
             self.mock_get_exploration_by_id):
             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.get_suggestion_by_id(
         self.suggestion_id)
     suggestion_services.reject_suggestion(
         suggestion, self.reviewer_id, 'reject review message')
     suggestion_services.resubmit_rejected_suggestion(
         suggestion, 'resubmit summary message', self.author_id)
     suggestion = suggestion_services.get_suggestion_by_id(
         self.suggestion_id)
     self.assertEqual(
         suggestion.status, suggestion_models.STATUS_IN_REVIEW)
Пример #6
0
    def get(self, thread_id):
        suggestion_id = thread_id
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        messages = [m.to_dict() for m in feedback_services.get_messages(
            thread_id)]
        message_ids = [message['message_id'] for message in messages]
        feedback_services.update_messages_read_by_the_user(
            self.user_id, thread_id, message_ids)
        self.values.update({
            'messages': messages,
            'suggestion': suggestion.to_dict() if suggestion else None
        })
        self.render_json(self.values)
Пример #7
0
    def put(self, suggestion_id):
        """Handles PUT requests.

        Args:
            suggestion_id: str. The ID of the suggestion.
        """
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)
        new_change = self.payload.get('change')
        change_cls = type(suggestion.change)
        change_object = change_cls(new_change)
        summary_message = self.payload.get('summary_message')
        suggestion_services.resubmit_rejected_suggestion(
            suggestion_id, summary_message, self.user_id, change_object)
        self.render_json(self.values)
    def test_accept_suggestion_successfully(self):
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            with self.swap(exp_services, 'get_exploration_by_id',
                           self.mock_get_exploration_by_id):
                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_cmd, 'test description',
                    self.assigned_reviewer_id, self.reviewer_id)

        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)

        with self.swap(exp_services, 'update_exploration',
                       self.check_commit_message):
            with self.swap(exp_services, 'get_exploration_by_id',
                           self.mock_get_exploration_by_id):
                with self.swap(suggestion_registry.SuggestionEditStateContent,
                               'pre_accept_validate', self.null_function):
                    with self.swap(
                            suggestion_registry.SuggestionEditStateContent,
                            'get_change_list_for_accepting_suggestion',
                            self.null_function):
                        suggestion_services.accept_suggestion(
                            suggestion, self.reviewer_id, self.COMMIT_MESSAGE,
                            'review message')
            suggestion = suggestion_services.get_suggestion_by_id(
                self.suggestion_id)
            self.assertEqual(suggestion.status,
                             suggestion_models.STATUS_ACCEPTED)
            self.assertEqual(suggestion.final_reviewer_id, self.reviewer_id)
            self.assertEqual(suggestion.assigned_reviewer_id, None)
            thread_messages = feedback_services.get_messages(self.THREAD_ID)
            last_message = thread_messages[len(thread_messages) - 1]
            self.assertEqual(last_message.text, 'review message')
    def test_reject_suggestion_handled_suggestion_failure(self):
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            with self.swap(exp_services, 'get_exploration_by_id',
                           self.mock_get_exploration_by_id):
                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_cmd, 'test description',
                    self.assigned_reviewer_id, self.reviewer_id)
        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)

        suggestion.status = suggestion_models.STATUS_ACCEPTED
        suggestion_services._update_suggestion(suggestion)  # pylint: disable=protected-access
        with self.assertRaisesRegexp(
                Exception,
                'The suggestion has already been accepted/rejected.'):
            suggestion_services.reject_suggestion(suggestion, self.reviewer_id,
                                                  'reject review message')

        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)
        self.assertEqual(suggestion.status, suggestion_models.STATUS_ACCEPTED)

        suggestion.status = suggestion_models.STATUS_REJECTED
        suggestion_services._update_suggestion(suggestion)  # pylint: disable=protected-access

        with self.assertRaisesRegexp(
                Exception,
                'The suggestion has already been accepted/rejected.'):
            suggestion_services.reject_suggestion(suggestion, self.reviewer_id,
                                                  'reject review message')
        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)
        self.assertEqual(suggestion.status, suggestion_models.STATUS_REJECTED)
Пример #10
0
    def test_accept_suggestion_to_topic(self):
        self.login(self.ADMIN_EMAIL)

        csrf_token = self.get_new_csrf_token()

        suggestion_to_accept = self.get_json(
            '%s?author_id=%s' % (
                feconf.SUGGESTION_LIST_URL_PREFIX,
                self.author_id))['suggestions'][0]

        suggestion = suggestion_services.get_suggestion_by_id(
            suggestion_to_accept['suggestion_id'])

        self.assertEqual(
            suggestion.status, suggestion_models.STATUS_IN_REVIEW)

        csrf_token = self.get_new_csrf_token()

        with self.swap(constants, 'ENABLE_NEW_STRUCTURE_PLAYERS', True):
            self.put_json('%s/topic/%s/%s' % (
                feconf.SUGGESTION_ACTION_URL_PREFIX,
                suggestion_to_accept['target_id'],
                suggestion_to_accept['suggestion_id']), {
                    'action': u'accept',
                    'commit_message': u'commit message',
                    'review_message': u'Accepted!',
                    'skill_id': self.skill_id
                }, csrf_token=csrf_token)

        suggestion = suggestion_services.get_suggestion_by_id(
            suggestion_to_accept['suggestion_id'])

        self.assertEqual(
            suggestion.status, suggestion_models.STATUS_ACCEPTED)

        self.logout()
Пример #11
0
    def post(self, thread_id):
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        text = self.payload.get('text')
        updated_status = self.payload.get('updated_status')
        if not text and not updated_status:
            raise self.InvalidInputException(
                'Text for the message must be specified.')
        if suggestion and updated_status:
            raise self.InvalidInputException(
                'Suggestion thread status cannot be changed manually.')

        feedback_services.create_message(
            thread_id, self.user_id, updated_status,
            self.payload.get('updated_subject'), text)
        self.render_json(self.values)
Пример #12
0
    def test_accept_suggestion_no_commit_message_failure(self):
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            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_cmd, self.score_category, 'test description',
                self.assigned_reviewer_id, self.reviewer_id)
        suggestion = suggestion_services.get_suggestion_by_id(
            self.suggestion_id)

        with self.assertRaisesRegexp(Exception,
                                     'Commit message cannot be empty.'):
            suggestion_services.accept_suggestion(suggestion, self.reviewer_id,
                                                  self.EMPTY_COMMIT_MESSAGE,
                                                  None)
Пример #13
0
    def create_translation_suggestion_for_exploration_0_and_verify(self):
        """Creates a translation suggestion for exploration 0 and performs basic
        assertions.
        """
        with self.swap(feedback_models.GeneralFeedbackThreadModel,
                       'generate_new_thread_id',
                       self.mock_generate_new_thread_id_for_suggestion):
            suggestion_services.create_suggestion(
                feconf.SUGGESTION_TYPE_TRANSLATE_CONTENT,
                feconf.ENTITY_TYPE_EXPLORATION, self.suggestion_target_id,
                self.suggestion_target_version_at_submission, self.owner_id,
                self.suggestion_change, 'test description')

        suggestion = suggestion_services.get_suggestion_by_id(self.THREAD_ID)

        self.assertIsNotNone(suggestion)
        self.assertEqual(suggestion.status, suggestion_models.STATUS_IN_REVIEW)
    def test_create_new_suggestion_successfully(self):
        expected_suggestion_dict = {
            'suggestion_id':
            'exploration.exp1.thread_1',
            'suggestion_type':
            (suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT),
            'target_type':
            suggestion_models.TARGET_TYPE_EXPLORATION,
            'target_id':
            self.target_id,
            'target_version_at_submission':
            self.target_version_at_submission,
            'status':
            suggestion_models.STATUS_IN_REVIEW,
            'author_name':
            'author',
            'final_reviewer_id':
            self.reviewer_id,
            'assigned_reviewer_id':
            self.assigned_reviewer_id,
            'change_cmd': {
                'cmd': exp_domain.CMD_EDIT_STATE_PROPERTY,
                'property_name': exp_domain.STATE_PROPERTY_CONTENT,
                'state_name': 'state_1',
                'new_value': 'new suggestion content',
                'old_value': None
            },
            'score_category':
            self.score_category
        }
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            with self.swap(exp_services, 'get_exploration_by_id',
                           self.mock_get_exploration_by_id):
                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_cmd, 'test description',
                    self.assigned_reviewer_id, self.reviewer_id)

            observed_suggestion = suggestion_services.get_suggestion_by_id(
                self.suggestion_id)
            self.assertDictContainsSubset(expected_suggestion_dict,
                                          observed_suggestion.to_dict())
Пример #15
0
    def put(self, target_id, suggestion_id):
        """Handles PUT requests.

        Args:
            target_id: str. The ID of the suggestion target.
            suggestion_id: str. The ID of the suggestion.
        """
        if suggestion_id.split('.')[0] != feconf.ENTITY_TYPE_SKILL:
            raise self.InvalidInputException(
                'This handler allows actions only on suggestions to skills.')

        if suggestion_id.split('.')[1] != target_id:
            raise self.InvalidInputException(
                'The skill id provided does not match the skill id present as '
                'part of the suggestion_id')

        action = self.payload.get('action')

        if action == constants.ACTION_ACCEPT_SUGGESTION:
            # Question suggestions do not use commit messages.
            suggestion_services.accept_suggestion(
                suggestion_id, self.user_id, 'UNUSED_COMMIT_MESSAGE',
                self.payload.get('review_message'))

            suggestion = suggestion_services.get_suggestion_by_id(
                suggestion_id)
            target_entity_html_list = (
                suggestion.get_target_entity_html_strings())
            target_image_filenames = (
                html_cleaner.get_image_filenames_from_html_strings(
                    target_entity_html_list))

            fs_services.copy_images(suggestion.target_type,
                                    suggestion.target_id,
                                    feconf.IMAGE_CONTEXT_QUESTION_SUGGESTIONS,
                                    suggestion.target_id,
                                    target_image_filenames)
        elif action == constants.ACTION_REJECT_SUGGESTION:
            suggestion_services.reject_suggestion(
                suggestion_id, self.user_id,
                self.payload.get('review_message'))
        else:
            raise self.InvalidInputException('Invalid action.')

        self.render_json(self.values)
Пример #16
0
    def post(self, suggestion_id):
        """Handles PUT requests.

        Raises:
            InvalidInputException. The suggestion is already handled.
            InvalidInputException. The 'skill_difficulty' parameter is missing.
            InvalidInputException. The 'skill_difficulty' is not a decimal.
            InvalidInputException. The 'question_state_data' parameter is
                missing.
            InvalidInputException. The 'question_state_data' parameter is
                invalid.
        """
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)
        if suggestion.is_handled:
            raise self.InvalidInputException(
                'The suggestion with id %s has been accepted or rejected' %
                (suggestion_id))

        if self.payload.get('skill_difficulty') is None:
            raise self.InvalidInputException(
                'The parameter \'skill_difficulty\' is missing.')

        if not isinstance(self.payload.get('skill_difficulty'), float):
            raise self.InvalidInputException(
                'The parameter \'skill_difficulty\' should be a decimal.')

        if self.payload.get('question_state_data') is None:
            raise self.InvalidInputException(
                'The parameter \'question_state_data\' is missing.')

        question_state_data_obj = state_domain.State.from_dict(
            self.payload.get('question_state_data'))
        question_state_data_obj.validate(None, False)

        updated_suggestion = suggestion_services.update_question_suggestion(
            suggestion_id, self.payload.get('skill_difficulty'),
            self.payload.get('question_state_data'))

        new_image_filenames = (utils.compute_list_difference(
            updated_suggestion.get_new_image_filenames_added_in_suggestion(),
            suggestion.get_new_image_filenames_added_in_suggestion()))
        _upload_suggestion_images(self.request, updated_suggestion,
                                  new_image_filenames)

        self.render_json(self.values)
Пример #17
0
    def put(self, target_id, suggestion_id):
        """Handles PUT requests.

        Args:
            target_id: str. The ID of the suggestion target.
            suggestion_id: str. The ID of the suggestion.
        """
        if (suggestion_id.split('.')[0] != feconf.ENTITY_TYPE_EXPLORATION):
            raise self.InvalidInputException(
                'This handler allows actions only'
                ' on suggestions to explorations.')

        if suggestion_id.split('.')[1] != target_id:
            raise self.InvalidInputException(
                'The exploration id provided does not match the exploration id '
                'present as part of the suggestion_id')

        action = self.payload.get('action')
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        if suggestion.author_id == self.user_id:
            raise self.UnauthorizedUserException(
                'You cannot accept/reject your own suggestion.')

        if action == constants.ACTION_ACCEPT_SUGGESTION:
            commit_message = self.payload.get('commit_message')
            if (commit_message is not None and
                    len(commit_message) > constants.MAX_COMMIT_MESSAGE_LENGTH):
                raise self.InvalidInputException(
                    'Commit messages must be at most %s characters long.' %
                    constants.MAX_COMMIT_MESSAGE_LENGTH)
            suggestion_services.accept_suggestion(
                suggestion_id, self.user_id,
                self.payload.get('commit_message'),
                self.payload.get('review_message'))
        elif action == constants.ACTION_REJECT_SUGGESTION:
            suggestion_services.reject_suggestion(
                suggestion_id, self.user_id,
                self.payload.get('review_message'))
        else:
            raise self.InvalidInputException('Invalid action.')

        self.render_json(self.values)
Пример #18
0
    def get(self, thread_id):
        if constants.USE_NEW_SUGGESTION_FRAMEWORK:
            suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        else:
            suggestion = feedback_services.get_suggestion(thread_id)

        messages = [
            m.to_dict() for m in feedback_services.get_messages(thread_id)
        ]
        message_ids = [message['message_id'] for message in messages]
        feedback_services.update_messages_read_by_the_user(
            self.user_id, thread_id, message_ids)
        self.values.update({
            'messages':
            messages,
            'suggestion':
            suggestion.to_dict() if suggestion else None
        })
        self.render_json(self.values)
Пример #19
0
    def get(self, thread_id):
        if not constants.ENABLE_GENERALIZED_FEEDBACK_THREADS:
            suggestion_id = 'exploration.%s' % thread_id
        else:
            suggestion_id = thread_id
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        messages = [
            m.to_dict() for m in feedback_services.get_messages(thread_id)
        ]
        message_ids = [message['message_id'] for message in messages]
        feedback_services.update_messages_read_by_the_user(
            self.user_id, thread_id, message_ids)
        self.values.update({
            'messages':
            messages,
            'suggestion':
            suggestion.to_dict() if suggestion else None
        })
        self.render_json(self.values)
Пример #20
0
 def test_resubmit_rejected_suggestion_failure(self):
     with self.swap(feedback_models.GeneralFeedbackThreadModel,
                    'generate_new_thread_id', self.generate_thread_id):
         with self.swap(exp_services, 'get_exploration_by_id',
                        self.mock_get_exploration_by_id):
             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.get_suggestion_by_id(
         self.suggestion_id)
     with self.assertRaisesRegexp(Exception,
                                  'Summary message cannot be empty.'):
         suggestion_services.resubmit_rejected_suggestion(
             suggestion, '', self.author_id)
     with self.assertRaisesRegexp(Exception,
                                  'The suggestion is not yet handled.'):
         suggestion_services.resubmit_rejected_suggestion(
             suggestion, 'resubmit summary message', self.author_id)
Пример #21
0
    def test_create_and_accept_suggestion(self):
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            suggestion_services.create_suggestion(
                suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
                suggestion_models.TARGET_TYPE_EXPLORATION, self.EXP_ID,
                self.target_version_at_submission, self.author_id,
                self.change_cmd, 'test description', None)

        suggestion_id = 'exploration.' + self.THREAD_ID
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        suggestion_services.accept_suggestion(suggestion, self.reviewer_id,
                                              self.COMMIT_MESSAGE, None)

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)

        self.assertEqual(exploration.states['State 1'].content.html,
                         'new content')

        self.assertEqual(suggestion.status, suggestion_models.STATUS_ACCEPTED)
Пример #22
0
    def get(self, thread_id):
        suggestion_id = thread_id
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        message_dicts = [
            message.to_dict()
            for message in feedback_services.get_messages(thread_id)
        ]
        message_ids = [message['message_id'] for message in message_dicts]
        if self.user_id:
            feedback_services.update_messages_read_by_the_user(
                self.user_id, thread_id, message_ids)

        self.values.update({
            'messages':
            replace_user_id_with_username_in_dict(
                message_dicts, [('author_id', 'author_username')]),
            'suggestion':
            suggestion.to_dict() if suggestion else None
        })
        self.render_json(self.values)
Пример #23
0
    def put(self, target_id, suggestion_id):
        if not constants.USE_NEW_SUGGESTION_FRAMEWORK:
            raise self.PageNotFoundException

        if len(suggestion_id.split('.')) != 3:
            raise self.InvalidInputException('Invalid format for suggestion_id.'
                                             ' It must contain 3 parts'
                                             ' separated by \'.\'')

        if (
                suggestion_id.split('.')[0] !=
                suggestion_models.TARGET_TYPE_EXPLORATION):
            raise self.InvalidInputException('This handler allows actions only'
                                             ' on suggestions to explorations.')

        if suggestion_id.split('.')[1] != target_id:
            raise self.InvalidInputException('The exploration id provided does '
                                             'not match the exploration id '
                                             'present as part of the '
                                             'suggestion_id')

        action = self.payload.get('action')
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        if suggestion.author_id == self.user_id:
            raise self.UnauthorizedUserException('You cannot accept/reject your'
                                                 ' own suggestion.')

        if action == suggestion_models.ACTION_TYPE_ACCEPT:
            suggestion_services.accept_suggestion(
                suggestion, self.user_id, self.payload.get('commit_message'),
                self.payload.get('review_message'))
        elif action == suggestion_models.ACTION_TYPE_REJECT:
            suggestion_services.reject_suggestion(
                suggestion, self.user_id, self.payload.get('review_message'))
        else:
            raise self.InvalidInputException('Invalid action.')

        self.render_json(self.values)
Пример #24
0
    def post(self, suggestion_id):
        """Handles PUT requests.

        Raises:
            InvalidInputException. The suggestion is already handled.
            InvalidInputException. The 'skill_difficulty' parameter is missing.
            InvalidInputException. The 'skill_difficulty' is not a decimal.
            InvalidInputException. The 'question_state_data' parameter is
                missing.
            InvalidInputException. The 'question_state_data' parameter is
                invalid.
        """
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)
        if suggestion.is_handled:
            raise self.InvalidInputException(
                'The suggestion with id %s has been accepted or rejected' %
                suggestion_id)

        if self.payload.get('skill_difficulty') is None:
            raise self.InvalidInputException(
                'The parameter \'skill_difficulty\' is missing.')

        if not isinstance(self.payload.get('skill_difficulty'), (float, int)):
            raise self.InvalidInputException(
                'The parameter \'skill_difficulty\' should be a decimal.')

        if self.payload.get('question_state_data') is None:
            raise self.InvalidInputException(
                'The parameter \'question_state_data\' is missing.')

        question_state_data_obj = state_domain.State.from_dict(
            self.payload.get('question_state_data'))
        question_state_data_obj.validate(None, False)

        suggestion_services.update_question_suggestion(
            suggestion_id, self.payload.get('skill_difficulty'),
            self.payload.get('question_state_data'))

        self.render_json(self.values)
Пример #25
0
    def post(self, thread_id):
        suggestion = suggestion_services.get_suggestion_by_id(thread_id)
        text = self.normalized_payload.get('text')
        updated_status = self.normalized_payload.get('updated_status')
        updated_subject = self.normalized_payload.get('updated_subject')

        if suggestion and updated_status:
            raise self.InvalidInputException(
                'Suggestion thread status cannot be changed manually.')

        messages = feedback_services.get_messages(thread_id)
        new_message = feedback_services.create_message(
            thread_id, self.user_id, updated_status, updated_subject, text)

        # Currently we are manually adding new message to the messages list as
        # the feedback_services.get_messages is not returning a correct list of
        # messages after adding new message model to the datastore because of an
        # unknown reason.
        messages.append(new_message)
        self.render_json({
            'messages': [message.to_dict() for message in messages]
        })
Пример #26
0
    def put(self, target_id, suggestion_id):
        if not constants.ENABLE_NEW_STRUCTURES:
            raise self.PageNotFoundException

        if len(suggestion_id.split('.')) != 3:
            raise self.InvalidInputException(
                'Invalid format for suggestion_id. It must contain 3 parts'
                ' separated by \'.\'')

        if suggestion_id.split('.')[0] != suggestion_models.TARGET_TYPE_TOPIC:
            raise self.InvalidInputException(
                'This handler allows actions only on suggestions to topics.')

        if suggestion_id.split('.')[1] != target_id:
            raise self.InvalidInputException(
                'The topic id provided does not match the topic id present as '
                'part of the suggestion_id')

        action = self.payload.get('action')
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        if action == suggestion_models.ACTION_TYPE_ACCEPT:
            if (
                    suggestion.suggestion_type ==
                    suggestion_models.SUGGESTION_TYPE_ADD_QUESTION):
                # The skill_id is passed only at the time of accepting the
                # suggestion.
                skill_id = self.payload.get('skill_id')
                suggestion.change.skill_id = skill_id
            suggestion_services.accept_suggestion(
                suggestion, self.user_id, self.payload.get('commit_message'),
                self.payload.get('review_message'))
        elif action == suggestion_models.ACTION_TYPE_REJECT:
            suggestion_services.reject_suggestion(
                suggestion, self.user_id, self.payload.get('review_message'))
        else:
            raise self.InvalidInputException('Invalid action.')

        self.render_json(self.values)
Пример #27
0
    def test_create_and_reject_suggestion(self):
        with self.swap(feedback_models.FeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            suggestion_services.create_suggestion(
                suggestion_models.SUGGESTION_TYPE_EDIT_STATE_CONTENT,
                suggestion_models.TARGET_TYPE_EXPLORATION, self.EXP_ID,
                self.target_version_at_submission, self.author_id,
                self.change_cmd, 'test description', None)

        suggestion_id = 'exploration.' + self.THREAD_ID
        suggestion = suggestion_services.get_suggestion_by_id(suggestion_id)

        suggestion_services.reject_suggestion(suggestion, self.reviewer_id,
                                              'Reject message')

        exploration = exp_services.get_exploration_by_id(self.EXP_ID)
        thread_messages = feedback_services.get_messages(self.THREAD_ID)
        last_message = thread_messages[len(thread_messages) - 1]
        self.assertEqual(last_message.text, 'Reject message')
        self.assertEqual(exploration.states['State 1'].content.html,
                         'old content')

        self.assertEqual(suggestion.status, suggestion_models.STATUS_REJECTED)
    def test_accept_suggestion_no_commit_message_failure(self):
        with self.swap(feedback_models.GeneralFeedbackThreadModel,
                       'generate_new_thread_id', self.generate_thread_id):
            with self.swap(exp_services, 'get_exploration_by_id',
                           self.mock_get_exploration_by_id):
                with self.swap(feconf, 'ENABLE_GENERALIZED_FEEDBACK_THREADS',
                               True):
                    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.get_suggestion_by_id(
            self.suggestion_id)

        with self.assertRaisesRegexp(Exception,
                                     'Commit message cannot be empty.'):
            with self.swap(feconf, 'ENABLE_GENERALIZED_FEEDBACK_THREADS',
                           True):
                suggestion_services.accept_suggestion(
                    suggestion, self.reviewer_id, self.EMPTY_COMMIT_MESSAGE,
                    None)
Пример #29
0
 def map(item):
     suggestion = suggestion_services.get_suggestion_by_id(item.id)
     try:
         suggestion.validate()
     except Exception as e:
         logging.error(
             'Suggestion %s failed validation: %s' % (item.id, e))
         yield (
             SuggestionMathMigrationOneOffJob._ERROR_KEY_BEFORE_MIGRATION,
             'Suggestion %s failed validation: %s' % (item.id, e))
         return
     html_string_list = suggestion.get_all_html_content_strings()
     html_string = ''.join(html_string_list)
     error_list = (
         html_validation_service.
         validate_math_tags_in_html_with_attribute_math_content(html_string))
     # Migrate the suggestion only if the suggestions have math-tags with
     # old schema.
     if len(error_list) > 0:
         suggestion.convert_html_in_suggestion_change(
             html_validation_service.add_math_content_to_math_rte_components)
         try:
             suggestion.validate()
         except Exception as e:
             logging.error(
                 'Suggestion %s failed validation after migration: %s' % (
                     item.id, e))
             yield (
                 SuggestionMathMigrationOneOffJob._ERROR_KEY_AFTER_MIGRATION,
                 'Suggestion %s failed validation: %s' % (
                     item.id, e))
             return
         item.change_cmd = suggestion.change.to_dict()
         item.update_timestamps(update_last_updated_time=False)
         item.put()
         yield ('suggestion_migrated', 1)
    def test_migration_job_does_not_convert_up_to_date_suggestion(self):
        suggestion_change = {
            'cmd': (question_domain.CMD_CREATE_NEW_FULLY_SPECIFIED_QUESTION),
            '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),
                'linked_skill_ids': [self.skill_id],
                'inapplicable_skill_misconception_ids': []
            },
            'skill_id': self.skill_id,
            'skill_difficulty': 0.3
        }

        suggestion = suggestion_services.create_suggestion(
            feconf.SUGGESTION_TYPE_ADD_QUESTION, feconf.ENTITY_TYPE_SKILL,
            self.skill_id, 1, self.albert_id, suggestion_change,
            'test description')

        self.assertEqual(
            suggestion.change.
            question_dict['question_state_data_schema_version'],
            feconf.CURRENT_STATE_SCHEMA_VERSION)

        expected_output = [u'[u\'SUCCESS\', 1]']
        self._run_job_and_verify_output(expected_output)

        updated_suggestion = suggestion_services.get_suggestion_by_id(
            suggestion.suggestion_id)
        self.assertEqual(
            updated_suggestion.change.
            question_dict['question_state_data_schema_version'],
            feconf.CURRENT_STATE_SCHEMA_VERSION)