示例#1
0
    def put(self, topic_id):
        """Updates properties of the given topic.
        Also, each change_dict given for editing should have an additional
        property called is_topic_change, which would be a boolean. If True, it
        means that change is for a topic (includes adding and removing
        subtopics), while False would mean it is for a Subtopic Page (this
        includes editing its html data as of now).
        """
        topic_domain.Topic.require_valid_topic_id(topic_id)
        topic = topic_fetchers.get_topic_by_id(topic_id, strict=False)

        version = self.payload.get('version')
        self._require_valid_version(version, topic.version)

        commit_message = self.payload.get('commit_message')
        topic_and_subtopic_page_change_dicts = self.payload.get(
            'topic_and_subtopic_page_change_dicts')
        topic_and_subtopic_page_change_list = []
        for change in topic_and_subtopic_page_change_dicts:
            if change['cmd'] == (
                    subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
                topic_and_subtopic_page_change_list.append(
                    subtopic_page_domain.SubtopicPageChange(change))
            else:
                topic_and_subtopic_page_change_list.append(
                    topic_domain.TopicChange(change))
        try:
            topic_services.update_topic_and_subtopic_pages(
                self.user_id, topic_id, topic_and_subtopic_page_change_list,
                commit_message)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        topic = topic_fetchers.get_topic_by_id(topic_id, strict=False)

        skill_id_to_description_dict, deleted_skill_ids = (
            skill_services.get_descriptions_of_skills(
                topic.get_all_skill_ids()))

        skill_id_to_rubrics_dict, deleted_skill_ids = (
            skill_services.get_rubrics_of_skills(topic.get_all_skill_ids()))

        if deleted_skill_ids:
            deleted_skills_string = ', '.join(deleted_skill_ids)
            logging.error(
                'The deleted skills: %s are still present in topic with id %s'
                % (deleted_skills_string, topic_id))
            if feconf.CAN_SEND_EMAILS:
                email_manager.send_mail_to_admin(
                    'Deleted skills present in topic',
                    'The deleted skills: %s are still present in topic with '
                    'id %s' % (deleted_skills_string, topic_id))

        self.values.update({
            'topic_dict': topic.to_dict(),
            'skill_id_to_description_dict': skill_id_to_description_dict,
            'skill_id_to_rubrics_dict': skill_id_to_rubrics_dict
        })

        self.render_json(self.values)
示例#2
0
 def setUp(self):
     super(BaseSubtopicViewerControllerTests, self).setUp()
     self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
     self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)
     self.set_admins([self.ADMIN_USERNAME])
     self.admin = user_services.UserActionsInfo(self.admin_id)
     self.topic_id = 'topic_id'
     self.subtopic_id = 1
     self.subtopic_page = (
         subtopic_page_domain.SubtopicPage.create_default_subtopic_page(
             self.subtopic_id, self.topic_id))
     subtopic_page_services.save_subtopic_page(
         self.user_id, self.subtopic_page, 'Added subtopic', [
             topic_domain.TopicChange({
                 'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                 'subtopic_id': self.subtopic_id,
                 'title': 'Sample'
             })
         ])
     self.content_ids_to_audio_translations_dict = {
         'content': {
             'en': {
                 'filename': 'test.mp3',
                 'file_size_bytes': 100,
                 'needs_update': False
             }
         }
     }
     self.written_translations_dict = {
         'translations_mapping': {
             'content': {}
         }
     }
     self.expected_page_contents_dict = {
         'content_ids_to_audio_translations':
         self.content_ids_to_audio_translations_dict,
         'subtitled_html': {
             'content_id': 'content',
             'html': 'hello world'
         }
     }
     self.subtopic_page.update_page_contents_html({
         'html': 'hello world',
         'content_id': 'content'
     })
     self.subtopic_page.update_page_contents_audio(
         self.content_ids_to_audio_translations_dict)
     subtopic_page_services.save_subtopic_page(
         self.user_id, self.subtopic_page, 'Updated page contents', [
             subtopic_page_domain.SubtopicPageChange(
                 {
                     'cmd':
                     subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                     'subtopic_id': self.subtopic_id,
                     'property_name': 'page_contents_html',
                     'new_value': 'a',
                     'old_value': 'b'
                 })
         ])
示例#3
0
 def test_subtopic_page_change_object_with_missing_attribute_in_cmd(self):
     with self.assertRaisesRegexp(
         utils.ValidationError, (
             'The following required attributes are missing: '
             'new_value, old_value')):
         subtopic_page_domain.SubtopicPageChange({
             'cmd': 'update_subtopic_page_property',
             'property_name': '<p>page_contents_html</p>',
         })
 def test_subtopic_page_change_object_with_extra_attribute_in_cmd(self):
     with self.assertRaisesRegexp(
             utils.ValidationError,
         ('The following extra attributes are present: invalid')):
         subtopic_page_domain.SubtopicPageChange({
             'cmd': 'create_new',
             'topic_id': 'topic_id',
             'subtopic_id': 'subtopic_id',
             'invalid': 'invalid'
         })
 def test_to_dict(self):
     subtopic_page_change_dict = {
         'cmd': 'create_new',
         'topic_id': 'topic_id',
         'subtopic_id': 'subtopic_id'
     }
     subtopic_page_change_object = subtopic_page_domain.SubtopicPageChange(
         subtopic_page_change_dict)
     self.assertEqual(subtopic_page_change_object.to_dict(),
                      subtopic_page_change_dict)
示例#6
0
 def test_get_subtopic_page_contents_by_id(self):
     self.subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
         self.TOPIC_ID, 1)
     recorded_voiceovers = {
         'voiceovers_mapping': {
             'content': {
                 'en': {
                     'filename': 'test.mp3',
                     'file_size_bytes': 100,
                     'needs_update': False,
                     'duration_secs': 7.213
                 }
             }
         }
     }
     expected_page_contents_dict = {
         'subtitled_html': {
             'content_id': 'content',
             'html': '<p>hello world</p>'
         },
         'recorded_voiceovers': recorded_voiceovers,
         'written_translations': {
             'translations_mapping': {
                 'content': {}
             }
         }
     }
     self.subtopic_page.update_page_contents_html(
         state_domain.SubtitledHtml.from_dict({
             'html': '<p>hello world</p>',
             'content_id': 'content'
         }))
     self.subtopic_page.update_page_contents_audio(
         state_domain.RecordedVoiceovers.from_dict(recorded_voiceovers))
     subtopic_page_services.save_subtopic_page(
         self.user_id, self.subtopic_page, 'Updated page contents', [
             subtopic_page_domain.SubtopicPageChange(
                 {
                     'cmd':
                     subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                     'subtopic_id': 1,
                     'property_name': 'page_contents_html',
                     'new_value': 'a',
                     'old_value': 'b'
                 })
         ])
     subtopic_page_contents = (
         subtopic_page_services.get_subtopic_page_contents_by_id(
             self.TOPIC_ID, 1))
     self.assertEqual(subtopic_page_contents.to_dict(),
                      expected_page_contents_dict)
     subtopic_page_contents = (
         subtopic_page_services.get_subtopic_page_contents_by_id(
             self.TOPIC_ID, 2, strict=False))
     self.assertEqual(subtopic_page_contents, None)
示例#7
0
    def test_subtopic_page_change_object_with_create_new(self):
        subtopic_page_change_object = (
            subtopic_page_domain.SubtopicPageChange({
                'cmd': 'create_new',
                'topic_id': 'topic_id',
                'subtopic_id': 'subtopic_id'
            }))

        self.assertEqual(subtopic_page_change_object.cmd, 'create_new')
        self.assertEqual(subtopic_page_change_object.topic_id, 'topic_id')
        self.assertEqual(subtopic_page_change_object.subtopic_id, 'subtopic_id')
示例#8
0
    def put(self, topic_id):
        """Updates properties of the given topic.
        Also, each change_dict given for editing should have an additional
        property called is_topic_change, which would be a boolean. If True, it
        means that change is for a topic (includes adding and removing
        subtopics), while False would mean it is for a Subtopic Page (this
        includes editing its html data as of now).
        """
        if not feconf.ENABLE_NEW_STRUCTURES:
            raise self.PageNotFoundException

        topic_domain.Topic.require_valid_topic_id(topic_id)
        topic = topic_services.get_topic_by_id(topic_id, strict=False)
        if topic is None:
            raise self.PageNotFoundException(
                Exception('The topic with the given id doesn\'t exist.'))

        version = self.payload.get('version')
        self._require_valid_version(version, topic.version)

        commit_message = self.payload.get('commit_message')
        topic_and_subtopic_page_change_dicts = self.payload.get(
            'topic_and_subtopic_page_change_dicts')
        topic_and_subtopic_page_change_list = []
        for change in topic_and_subtopic_page_change_dicts:
            if change['change_affects_subtopic_page']:
                topic_and_subtopic_page_change_list.append(
                    subtopic_page_domain.SubtopicPageChange(change))
            else:
                topic_and_subtopic_page_change_list.append(
                    topic_domain.TopicChange(change))
        try:
            topic_services.update_topic_and_subtopic_pages(
                self.user_id, topic_id, topic_and_subtopic_page_change_list,
                commit_message)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        topic = topic_services.get_topic_by_id(topic_id, strict=False)
        skill_ids = topic.get_all_skill_ids()

        skill_id_to_description_dict = (
            skill_services.get_skill_descriptions_by_ids(topic_id, skill_ids))

        self.values.update({
            'topic_dict':
            topic.to_dict(),
            'skill_id_to_description_dict':
            skill_id_to_description_dict
        })

        self.render_json(self.values)
 def test_get_subtopic_page_contents_by_id(self):
     self.subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
         self.TOPIC_ID, 1)
     content_ids_to_audio_translations_dict = {
         'content': {
             'en': {
                 'filename': 'test.mp3',
                 'file_size_bytes': 100,
                 'needs_update': False
             }
         }
     }
     expected_page_contents_dict = {
         'content_ids_to_audio_translations':
         content_ids_to_audio_translations_dict,
         'subtitled_html': {
             'content_id': 'content',
             'html': 'hello world'
         },
         'written_translations': {
             'translations_mapping': {
                 'content': {}
             }
         }
     }
     self.subtopic_page.update_page_contents_html({
         'html': 'hello world',
         'content_id': 'content'
     })
     self.subtopic_page.update_page_contents_audio(
         content_ids_to_audio_translations_dict)
     subtopic_page_services.save_subtopic_page(
         self.user_id, self.subtopic_page, 'Updated page contents', [
             subtopic_page_domain.SubtopicPageChange(
                 {
                     'cmd':
                     subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                     'subtopic_id': 1,
                     'property_name': 'page_contents_html',
                     'new_value': 'a',
                     'old_value': 'b'
                 })
         ])
     subtopic_page_contents = (
         subtopic_page_services.get_subtopic_page_contents_by_id(
             self.TOPIC_ID, 1))
     self.assertEqual(subtopic_page_contents.to_dict(),
                      expected_page_contents_dict)
     subtopic_page_contents = (
         subtopic_page_services.get_subtopic_page_contents_by_id(
             self.TOPIC_ID, 2, strict=False))
     self.assertEqual(subtopic_page_contents, None)
 def test_subtopic_page_change_object_with_invalid_subtopic_page_property(
         self):
     with self.assertRaisesRegexp(
             utils.ValidationError,
         ('Value for property_name in cmd update_subtopic_page_property: '
          'invalid is not allowed')):
         subtopic_page_domain.SubtopicPageChange({
             'cmd': 'update_subtopic_page_property',
             'subtopic_id': 'subtopic_id',
             'property_name': 'invalid',
             'old_value': 'old_value',
             'new_value': 'new_value',
         })
示例#11
0
    def test_create_subtopic_page_change(self):
        subtopic_page_change_object = subtopic_page_domain.SubtopicPageChange({
            'cmd': subtopic_page_domain.CMD_CREATE_NEW,
            'topic_id': self.topic_id,
            'subtopic_id': 'subtopic_id'
        })

        self.assertEqual(
            subtopic_page_change_object.to_dict(), {
                'cmd': subtopic_page_domain.CMD_CREATE_NEW,
                'topic_id': self.topic_id,
                'subtopic_id': 'subtopic_id'
            })
示例#12
0
 def test_standard_operation(self):
     topic_services.update_topic_and_subtopic_pages(
         self.owner_id, '0', [subtopic_page_domain.SubtopicPageChange({
             'cmd': 'update_subtopic_page_property',
             'property_name': 'page_contents_html',
             'subtopic_id': 1,
             'new_value': {
                 'html': '<p>html</p>',
                 'content_id': 'content'
             },
             'old_value': {}
         })], 'Changes.')
     expected_output = [
         u'[u\'fully-validated SubtopicPageCommitLogEntryModel\', 4]']
     self.run_job_and_check_output(
         expected_output, sort=False, literal_eval=False)
示例#13
0
    def put(self, topic_id):
        """Updates properties of the given topic.
        Also, each change_dict given for editing should have an additional
        property called is_topic_change, which would be a boolean. If True, it
        means that change is for a topic (includes adding and removing
        subtopics), while False would mean it is for a Subtopic Page (this
        includes editing its html data as of now).
        """
        topic_domain.Topic.require_valid_topic_id(topic_id)
        topic = topic_services.get_topic_by_id(topic_id, strict=False)

        version = self.payload.get('version')
        self._require_valid_version(version, topic.version)

        commit_message = self.payload.get('commit_message')
        topic_and_subtopic_page_change_dicts = self.payload.get(
            'topic_and_subtopic_page_change_dicts')
        topic_and_subtopic_page_change_list = []
        for change in topic_and_subtopic_page_change_dicts:
            if change['cmd'] == (
                    subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
                topic_and_subtopic_page_change_list.append(
                    subtopic_page_domain.SubtopicPageChange(change))
            else:
                topic_and_subtopic_page_change_list.append(
                    topic_domain.TopicChange(change))
        try:
            topic_services.update_topic_and_subtopic_pages(
                self.user_id, topic_id, topic_and_subtopic_page_change_list,
                commit_message)
        except utils.ValidationError as e:
            raise self.InvalidInputException(e)

        topic = topic_services.get_topic_by_id(topic_id, strict=False)
        skill_ids = topic.get_all_skill_ids()

        skill_id_to_description_dict = (
            skill_services.get_skill_descriptions_by_ids(topic_id, skill_ids))

        self.values.update({
            'topic_dict':
            topic.to_dict(),
            'skill_id_to_description_dict':
            skill_id_to_description_dict
        })

        self.render_json(self.values)
示例#14
0
    def test_subtopic_page_change_object_with_update_subtopic_page_property(
            self):
        subtopic_page_change_object = subtopic_page_domain.SubtopicPageChange({
            'cmd': 'update_subtopic_page_property',
            'subtopic_id': 'subtopic_id',
            'property_name': 'page_contents_html',
            'new_value': 'new_value',
            'old_value': 'old_value'
        })

        self.assertEqual(
            subtopic_page_change_object.cmd, 'update_subtopic_page_property')
        self.assertEqual(subtopic_page_change_object.subtopic_id, 'subtopic_id')
        self.assertEqual(
            subtopic_page_change_object.property_name, 'page_contents_html')
        self.assertEqual(subtopic_page_change_object.new_value, 'new_value')
        self.assertEqual(subtopic_page_change_object.old_value, 'old_value')
示例#15
0
    def test_update_topic_and_subtopic_page(self):
        changelist = [topic_domain.TopicChange({
            'cmd': topic_domain.CMD_ADD_SUBTOPIC,
            'title': 'Title3',
            'subtopic_id': 3
        })]
        with self.assertRaisesRegexp(
            Exception, 'The given new subtopic id 3 is not equal to '
            'the expected next subtopic id: 2'):
            topic_services.update_topic_and_subtopic_pages(
                self.user_id_admin, self.TOPIC_ID, changelist,
                'Added subtopic.')

        # Test whether the subtopic page was created for the above failed
        # attempt.
        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 3, strict=False)
        self.assertIsNone(subtopic_page)

        # Test exception raised for simultaneous adding and removing of
        # subtopics.
        changelist = [
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                'title': 'Title2',
                'subtopic_id': 2
            }),
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_DELETE_SUBTOPIC,
                'subtopic_id': 2
            })
        ]
        with self.assertRaisesRegexp(
            Exception, 'The incoming changelist had simultaneous'
            ' creation and deletion of subtopics.'):
            topic_services.update_topic_and_subtopic_pages(
                self.user_id_admin, self.TOPIC_ID, changelist,
                'Added and deleted a subtopic.')

        # Test whether a subtopic page already existing in datastore can be
        # edited.
        changelist = [subtopic_page_domain.SubtopicPageChange({
            'cmd': subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
            'property_name': (
                subtopic_page_domain.SUBTOPIC_PAGE_PROPERTY_HTML_DATA),
            'old_value': '',
            'subtopic_id': 1,
            'new_value': '<p>New Value</p>'
        })]
        topic_services.update_topic_and_subtopic_pages(
            self.user_id_admin, self.TOPIC_ID, changelist,
            'Updated html data')
        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 1)
        self.assertEqual(subtopic_page.html_data, '<p>New Value</p>')

        # Test a sequence of changes with both topic and subtopic page changes.
        changelist = [
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                'title': 'Title2',
                'subtopic_id': 2
            }),
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_DELETE_SUBTOPIC,
                'subtopic_id': 1
            }),
            subtopic_page_domain.SubtopicPageChange({
                'cmd': subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                'property_name': (
                    subtopic_page_domain.SUBTOPIC_PAGE_PROPERTY_HTML_DATA),
                'old_value': '',
                'subtopic_id': 2,
                'new_value': '<p>New Value</p>'
            }),
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_MOVE_SKILL_ID_TO_SUBTOPIC,
                'old_subtopic_id': None,
                'new_subtopic_id': 2,
                'skill_id': self.skill_id_1
            })
        ]
        topic_services.update_topic_and_subtopic_pages(
            self.user_id_admin, self.TOPIC_ID, changelist,
            'Added and removed a subtopic.')
        topic = topic_services.get_topic_by_id(self.TOPIC_ID)
        self.assertEqual(len(topic.subtopics), 1)
        self.assertEqual(topic.next_subtopic_id, 3)
        self.assertEqual(topic.subtopics[0].title, 'Title2')
        self.assertEqual(topic.subtopics[0].skill_ids, [self.skill_id_1])

        # Test whether the subtopic page corresponding to the deleted subtopic
        # was also deleted.
        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 1, strict=False)
        self.assertIsNone(subtopic_page)
        # Validate the newly created subtopic page.
        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 2, strict=False)
        self.assertEqual(subtopic_page.html_data, '<p>New Value</p>')

        # Making sure everything resets when an error is encountered anywhere.
        changelist = [
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                'title': 'Title3',
                'subtopic_id': 3
            }),
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                'title': 'Title4',
                'subtopic_id': 4
            }),
            topic_domain.TopicChange({
                'cmd': topic_domain.CMD_DELETE_SUBTOPIC,
                'subtopic_id': 2
            }),
            # The following is an invalid command as subtopic with id 2 was
            # deleted in previous step.
            subtopic_page_domain.SubtopicPageChange({
                'cmd': subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                'property_name': (
                    subtopic_page_domain.SUBTOPIC_PAGE_PROPERTY_HTML_DATA),
                'old_value': '',
                'subtopic_id': 2,
                'new_value': '<p>New Value</p>'
            }),
        ]
        with self.assertRaisesRegexp(
            Exception, 'The subtopic with id 2 doesn\'t exist'):
            topic_services.update_topic_and_subtopic_pages(
                self.user_id_admin, self.TOPIC_ID, changelist,
                'Done some changes.')

        # Make sure the topic object in datastore is not affected.
        topic = topic_services.get_topic_by_id(self.TOPIC_ID)
        self.assertEqual(len(topic.subtopics), 1)
        self.assertEqual(topic.next_subtopic_id, 3)
        self.assertEqual(topic.subtopics[0].title, 'Title2')
        self.assertEqual(topic.subtopics[0].skill_ids, [self.skill_id_1])

        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 3, strict=False)
        self.assertIsNone(subtopic_page)
        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 4, strict=False)
        self.assertIsNone(subtopic_page)
        subtopic_page = subtopic_page_services.get_subtopic_page_by_id(
            self.TOPIC_ID, 2, strict=False)
        self.assertIsNotNone(subtopic_page)
示例#16
0
 def test_subtopic_page_change_object_with_missing_cmd(self) -> None:
     with self.assertRaisesRegex(  # type: ignore[no-untyped-call]
         utils.ValidationError, 'Missing cmd key in change dict'):
         subtopic_page_domain.SubtopicPageChange({'invalid': 'data'})
 def test_subtopic_page_change_object_with_invalid_cmd(self):
     with self.assertRaisesRegexp(utils.ValidationError,
                                  'Command invalid is not allowed'):
         subtopic_page_domain.SubtopicPageChange({'cmd': 'invalid'})
 def test_subtopic_page_change_object_with_missing_cmd(self):
     with self.assertRaisesRegexp(utils.ValidationError,
                                  'Missing cmd key in change dict'):
         subtopic_page_domain.SubtopicPageChange({'invalid': 'data'})
示例#19
0
 def test_subtopic_page_change_object_with_invalid_cmd(self) -> None:
     with self.assertRaisesRegex(  # type: ignore[no-untyped-call]
         utils.ValidationError, 'Command invalid is not allowed'):
         subtopic_page_domain.SubtopicPageChange({'cmd': 'invalid'})
示例#20
0
def apply_change_list(topic_id, change_list):
    """Applies a changelist to a topic and returns the result. The incoming
    changelist should not have simultaneuous creations and deletion of
    subtopics.

    Args:
        topic_id: str. ID of the given topic.
        change_list: list(TopicChange). A change list to be applied to the given
            topic.

    Raises:
        Exception. The incoming changelist had simultaneuous creation and
            deletion of subtopics.

    Returns:
        tuple(Topic, dict, list(int), list(int), list(SubtopicPageChange)). The
        modified topic object, the modified subtopic pages dict keyed
        by subtopic page id containing the updated domain objects of
        each subtopic page, a list of ids of the deleted subtopics,
        a list of ids of the newly created subtopics and a list of changes
        applied to modified subtopic pages.
    """
    topic = topic_fetchers.get_topic_by_id(topic_id)
    newly_created_subtopic_ids = []
    existing_subtopic_page_ids_to_be_modified = []
    deleted_subtopic_ids = []
    modified_subtopic_pages_list = []
    modified_subtopic_pages = {}
    modified_subtopic_change_cmds = collections.defaultdict(list)

    for change in change_list:
        if (change.cmd ==
                subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
            if change.subtopic_id < topic.next_subtopic_id:
                existing_subtopic_page_ids_to_be_modified.append(
                    change.subtopic_id)
                subtopic_page_id = (
                    subtopic_page_domain.SubtopicPage.get_subtopic_page_id(
                        topic_id, change.subtopic_id))
                modified_subtopic_change_cmds[subtopic_page_id].append(change)
    modified_subtopic_pages_list = (
        subtopic_page_services.get_subtopic_pages_with_ids(
            topic_id, existing_subtopic_page_ids_to_be_modified))
    for subtopic_page in modified_subtopic_pages_list:
        modified_subtopic_pages[subtopic_page.id] = subtopic_page

    try:
        for change in change_list:
            if change.cmd == topic_domain.CMD_ADD_SUBTOPIC:
                topic.add_subtopic(change.subtopic_id, change.title)
                subtopic_page_id = (
                    subtopic_page_domain.SubtopicPage.get_subtopic_page_id(
                        topic_id, change.subtopic_id))
                modified_subtopic_pages[subtopic_page_id] = (
                    subtopic_page_domain.SubtopicPage.
                    create_default_subtopic_page(  # pylint: disable=line-too-long
                        change.subtopic_id, topic_id))
                modified_subtopic_change_cmds[subtopic_page_id].append(
                    subtopic_page_domain.SubtopicPageChange({
                        'cmd':
                        'create_new',
                        'topic_id':
                        topic_id,
                        'subtopic_id':
                        change.subtopic_id
                    }))
                newly_created_subtopic_ids.append(change.subtopic_id)
            elif change.cmd == topic_domain.CMD_DELETE_SUBTOPIC:
                topic.delete_subtopic(change.subtopic_id)
                if change.subtopic_id in newly_created_subtopic_ids:
                    raise Exception('The incoming changelist had simultaneous'
                                    ' creation and deletion of subtopics.')
                deleted_subtopic_ids.append(change.subtopic_id)
            elif change.cmd == topic_domain.CMD_ADD_CANONICAL_STORY:
                topic.add_canonical_story(change.story_id)
            elif change.cmd == topic_domain.CMD_DELETE_CANONICAL_STORY:
                topic.delete_canonical_story(change.story_id)
            elif change.cmd == topic_domain.CMD_REARRANGE_CANONICAL_STORY:
                topic.rearrange_canonical_story(change.from_index,
                                                change.to_index)
            elif change.cmd == topic_domain.CMD_ADD_ADDITIONAL_STORY:
                topic.add_additional_story(change.story_id)
            elif change.cmd == topic_domain.CMD_DELETE_ADDITIONAL_STORY:
                topic.delete_additional_story(change.story_id)
            elif change.cmd == topic_domain.CMD_ADD_UNCATEGORIZED_SKILL_ID:
                topic.add_uncategorized_skill_id(
                    change.new_uncategorized_skill_id)
            elif change.cmd == topic_domain.CMD_REMOVE_UNCATEGORIZED_SKILL_ID:
                topic.remove_uncategorized_skill_id(
                    change.uncategorized_skill_id)
            elif change.cmd == topic_domain.CMD_MOVE_SKILL_ID_TO_SUBTOPIC:
                topic.move_skill_id_to_subtopic(change.old_subtopic_id,
                                                change.new_subtopic_id,
                                                change.skill_id)
            elif change.cmd == topic_domain.CMD_REARRANGE_SKILL_IN_SUBTOPIC:
                topic.rearrange_skill_in_subtopic(change.subtopic_id,
                                                  change.from_index,
                                                  change.to_index)
            elif change.cmd == topic_domain.CMD_REARRANGE_SUBTOPIC:
                topic.rearrange_subtopic(change.from_index, change.to_index)
            elif change.cmd == topic_domain.CMD_REMOVE_SKILL_ID_FROM_SUBTOPIC:
                topic.remove_skill_id_from_subtopic(change.subtopic_id,
                                                    change.skill_id)
            elif change.cmd == topic_domain.CMD_UPDATE_TOPIC_PROPERTY:
                if (change.property_name == topic_domain.TOPIC_PROPERTY_NAME):
                    topic.update_name(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_ABBREVIATED_NAME):
                    topic.update_abbreviated_name(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_URL_FRAGMENT):
                    topic.update_url_fragment(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_DESCRIPTION):
                    topic.update_description(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_LANGUAGE_CODE):
                    topic.update_language_code(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_THUMBNAIL_FILENAME):
                    topic.update_thumbnail_filename(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_THUMBNAIL_BG_COLOR):
                    topic.update_thumbnail_bg_color(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_META_TAG_CONTENT):
                    topic.update_meta_tag_content(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_PRACTICE_TAB_IS_DISPLAYED):
                    topic.update_practice_tab_is_displayed(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_PAGE_TITLE_FRAGMENT_FOR_WEB):
                    topic.update_page_title_fragment_for_web(change.new_value)
            elif (change.cmd ==
                  subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
                subtopic_page_id = (
                    subtopic_page_domain.SubtopicPage.get_subtopic_page_id(
                        topic_id, change.subtopic_id))
                if ((modified_subtopic_pages[subtopic_page_id] is None)
                        or (change.subtopic_id in deleted_subtopic_ids)):
                    raise Exception('The subtopic with id %s doesn\'t exist' %
                                    (change.subtopic_id))

                if (change.property_name == subtopic_page_domain.
                        SUBTOPIC_PAGE_PROPERTY_PAGE_CONTENTS_HTML):
                    page_contents = state_domain.SubtitledHtml.from_dict(
                        change.new_value)
                    page_contents.validate()
                    modified_subtopic_pages[
                        subtopic_page_id].update_page_contents_html(
                            page_contents)

                elif (change.property_name == subtopic_page_domain.
                      SUBTOPIC_PAGE_PROPERTY_PAGE_CONTENTS_AUDIO):
                    modified_subtopic_pages[
                        subtopic_page_id].update_page_contents_audio(
                            state_domain.RecordedVoiceovers.from_dict(
                                change.new_value))
            elif change.cmd == topic_domain.CMD_UPDATE_SUBTOPIC_PROPERTY:
                if (change.property_name ==
                        topic_domain.SUBTOPIC_PROPERTY_TITLE):
                    topic.update_subtopic_title(change.subtopic_id,
                                                change.new_value)
                if (change.property_name ==
                        topic_domain.SUBTOPIC_PROPERTY_THUMBNAIL_FILENAME):
                    topic.update_subtopic_thumbnail_filename(
                        change.subtopic_id, change.new_value)
                if (change.property_name ==
                        topic_domain.SUBTOPIC_PROPERTY_THUMBNAIL_BG_COLOR):
                    topic.update_subtopic_thumbnail_bg_color(
                        change.subtopic_id, change.new_value)
                if (change.property_name ==
                        topic_domain.SUBTOPIC_PROPERTY_URL_FRAGMENT):
                    topic.update_subtopic_url_fragment(change.subtopic_id,
                                                       change.new_value)

            elif (change.cmd ==
                  topic_domain.CMD_MIGRATE_SUBTOPIC_SCHEMA_TO_LATEST_VERSION):
                # Loading the topic model from the datastore into a
                # Topic domain object automatically converts it to use the
                # latest schema version. As a result, simply resaving the
                # topic is sufficient to apply the schema migration.
                continue
        return (topic, modified_subtopic_pages, deleted_subtopic_ids,
                newly_created_subtopic_ids, modified_subtopic_change_cmds)

    except Exception as e:
        logging.error('%s %s %s %s' %
                      (e.__class__.__name__, e, topic_id, change_list))
        python_utils.reraise_exception()
示例#21
0
def apply_change_list(topic_id, change_list):
    """Applies a changelist to a topic and returns the result. The incoming
    changelist should not have simultaneuous creations and deletion of
    subtopics.

    Args:
        topic_id: str. ID of the given topic.
        change_list: list(TopicChange). A change list to be applied to the given
            topic.

    Raises:
        Exception. The incoming changelist had simultaneuous creation and
            deletion of subtopics.
    Returns:
        Topic, dict, list(int), list(int), list(SubtopicPageChange).
            The modified topic object, the modified subtopic pages dict keyed
            by subtopic page id containing the updated domain objects of
            each subtopic page, a list of ids of the deleted subtopics,
            a list of ids of the newly created subtopics and a list of changes
            applied to modified subtopic pages.
    """
    topic = get_topic_by_id(topic_id)
    newly_created_subtopic_ids = []
    existing_subtopic_page_ids_to_be_modified = []
    deleted_subtopic_ids = []
    modified_subtopic_pages_list = []
    modified_subtopic_pages = {}
    modified_subtopic_change_cmds = collections.defaultdict(list)

    for change in change_list:
        if (change.cmd ==
                subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
            if change.subtopic_id < topic.next_subtopic_id:
                existing_subtopic_page_ids_to_be_modified.append(
                    change.subtopic_id)
                subtopic_page_id = (
                    subtopic_page_domain.SubtopicPage.get_subtopic_page_id(
                        topic_id, change.subtopic_id))
                modified_subtopic_change_cmds[subtopic_page_id].append(change)
    modified_subtopic_pages_list = (
        subtopic_page_services.get_subtopic_pages_with_ids(
            topic_id, existing_subtopic_page_ids_to_be_modified))
    for subtopic_page in modified_subtopic_pages_list:
        modified_subtopic_pages[subtopic_page.id] = subtopic_page
    try:
        for change in change_list:
            if change.cmd == topic_domain.CMD_ADD_SUBTOPIC:
                topic.add_subtopic(change.subtopic_id, change.title)
                subtopic_page_id = (
                    subtopic_page_domain.SubtopicPage.get_subtopic_page_id(
                        topic_id, change.subtopic_id))
                modified_subtopic_pages[subtopic_page_id] = (
                    subtopic_page_domain.SubtopicPage.
                    create_default_subtopic_page(  #pylint: disable=line-too-long
                        change.subtopic_id, topic_id))
                modified_subtopic_change_cmds[subtopic_page_id].append(
                    subtopic_page_domain.SubtopicPageChange({
                        'cmd':
                        'create_new',
                        'topic_id':
                        topic_id,
                        'subtopic_id':
                        change.subtopic_id
                    }))
                newly_created_subtopic_ids.append(change.subtopic_id)
            elif change.cmd == topic_domain.CMD_DELETE_SUBTOPIC:
                topic.delete_subtopic(change.subtopic_id)
                if change.subtopic_id in newly_created_subtopic_ids:
                    raise Exception('The incoming changelist had simultaneous'
                                    ' creation and deletion of subtopics.')
                deleted_subtopic_ids.append(change.subtopic_id)
            elif change.cmd == topic_domain.CMD_ADD_UNCATEGORIZED_SKILL_ID:
                topic.add_uncategorized_skill_id(
                    change.new_uncategorized_skill_id)
            elif change.cmd == topic_domain.CMD_REMOVE_UNCATEGORIZED_SKILL_ID:
                topic.remove_uncategorized_skill_id(
                    change.uncategorized_skill_id)
            elif change.cmd == topic_domain.CMD_MOVE_SKILL_ID_TO_SUBTOPIC:
                topic.move_skill_id_to_subtopic(change.old_subtopic_id,
                                                change.new_subtopic_id,
                                                change.skill_id)
            elif change.cmd == topic_domain.CMD_REMOVE_SKILL_ID_FROM_SUBTOPIC:
                topic.remove_skill_id_from_subtopic(change.subtopic_id,
                                                    change.skill_id)
            elif change.cmd == topic_domain.CMD_UPDATE_TOPIC_PROPERTY:
                if (change.property_name == topic_domain.TOPIC_PROPERTY_NAME):
                    topic.update_name(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_DESCRIPTION):
                    topic.update_description(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_CANONICAL_STORY_IDS):
                    topic.update_canonical_story_ids(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_ADDITIONAL_STORY_IDS):
                    topic.update_additional_story_ids(change.new_value)
                elif (change.property_name ==
                      topic_domain.TOPIC_PROPERTY_LANGUAGE_CODE):
                    topic.update_language_code(change.new_value)
            elif (change.cmd ==
                  subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
                subtopic_page_id = (
                    subtopic_page_domain.SubtopicPage.get_subtopic_page_id(
                        topic_id, change.subtopic_id))
                if ((modified_subtopic_pages[subtopic_page_id] is None)
                        or (change.subtopic_id in deleted_subtopic_ids)):
                    raise Exception('The subtopic with id %s doesn\'t exist' %
                                    (change.subtopic_id))

                if (change.property_name == subtopic_page_domain.
                        SUBTOPIC_PAGE_PROPERTY_PAGE_CONTENTS_HTML):
                    modified_subtopic_pages[
                        subtopic_page_id].update_page_contents_html(
                            change.new_value)

                elif (change.property_name == subtopic_page_domain.
                      SUBTOPIC_PAGE_PROPERTY_PAGE_CONTENTS_AUDIO):
                    modified_subtopic_pages[
                        subtopic_page_id].update_page_contents_audio(
                            change.new_value)
            elif change.cmd == topic_domain.CMD_UPDATE_SUBTOPIC_PROPERTY:
                if (change.property_name ==
                        topic_domain.SUBTOPIC_PROPERTY_TITLE):
                    topic.update_subtopic_title(change.subtopic_id,
                                                change.new_value)
            elif (change.cmd ==
                  topic_domain.CMD_MIGRATE_SUBTOPIC_SCHEMA_TO_LATEST_VERSION):
                # Loading the topic model from the datastore into a
                # Topic domain object automatically converts it to use the
                # latest schema version. As a result, simply resaving the
                # topic is sufficient to apply the schema migration.
                continue
        return (topic, modified_subtopic_pages, deleted_subtopic_ids,
                newly_created_subtopic_ids, modified_subtopic_change_cmds)

    except Exception as e:
        logging.error('%s %s %s %s' %
                      (e.__class__.__name__, e, topic_id, change_list))
        raise
示例#22
0
 def setUp(self):
     super(BaseSubtopicViewerControllerTests, self).setUp()
     self.signup(self.ADMIN_EMAIL, self.ADMIN_USERNAME)
     self.admin_id = self.get_user_id_from_email(self.ADMIN_EMAIL)
     self.set_admins([self.ADMIN_USERNAME])
     self.admin = user_services.UserActionsInfo(self.admin_id)
     self.topic_id = 'topic_id'
     self.subtopic_id = 1
     self.subtopic_page = (
         subtopic_page_domain.SubtopicPage.create_default_subtopic_page(
             self.subtopic_id, self.topic_id))
     subtopic_page_services.save_subtopic_page(
         self.admin_id, self.subtopic_page, 'Added subtopic',
         [topic_domain.TopicChange({
             'cmd': topic_domain.CMD_ADD_SUBTOPIC,
             'subtopic_id': self.subtopic_id,
             'title': 'Sample'
         })]
     )
     subtopic_page_2 = (
         subtopic_page_domain.SubtopicPage.create_default_subtopic_page(
             self.subtopic_id, 'topic_id_2'))
     subtopic_page_services.save_subtopic_page(
         self.admin_id, subtopic_page_2, 'Added subtopic',
         [topic_domain.TopicChange({
             'cmd': topic_domain.CMD_ADD_SUBTOPIC,
             'subtopic_id': self.subtopic_id,
             'title': 'Sample'
         })]
     )
     subtopic = topic_domain.Subtopic.create_default_subtopic(
         1, 'Subtopic Title')
     self.save_new_topic(
         self.topic_id, self.admin_id, 'Name', 'Description', [], [],
         [], [subtopic], 2)
     topic_services.publish_topic(self.topic_id, self.admin_id)
     self.save_new_topic(
         'topic_id_2', self.admin_id, 'Private_Name', 'Description', [], [],
         [], [subtopic], 2)
     self.recorded_voiceovers_dict = {
         'voiceovers_mapping': {
             'content': {
                 'en': {
                     'filename': 'test.mp3',
                     'file_size_bytes': 100,
                     'needs_update': False
                 }
             }
         }
     }
     self.written_translations_dict = {
         'translations_mapping': {
             'content': {}
         }
     }
     self.subtopic_page.update_page_contents_html(
         state_domain.SubtitledHtml.from_dict({
             'html': '<p>hello world</p>',
             'content_id': 'content'
         }))
     self.subtopic_page.update_page_contents_audio(
         state_domain.RecordedVoiceovers.from_dict(
             self.recorded_voiceovers_dict))
     subtopic_page_services.save_subtopic_page(
         self.admin_id, self.subtopic_page, 'Updated page contents',
         [subtopic_page_domain.SubtopicPageChange({
             'cmd': subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
             'subtopic_id': self.subtopic_id,
             'property_name': 'page_contents_html',
             'new_value': 'a',
             'old_value': 'b'
         })]
     )
示例#23
0
    def setUp(self):
        super(BaseSubtopicViewerControllerTests, self).setUp()
        self.signup(self.CURRICULUM_ADMIN_EMAIL,
                    self.CURRICULUM_ADMIN_USERNAME)
        self.admin_id = self.get_user_id_from_email(
            self.CURRICULUM_ADMIN_EMAIL)
        self.set_curriculum_admins([self.CURRICULUM_ADMIN_USERNAME])
        self.admin = user_services.get_user_actions_info(self.admin_id)
        self.topic_id = 'topic_id'
        self.subtopic_id_1 = 1
        self.subtopic_id_2 = 2
        self.subtopic_page_1 = (
            subtopic_page_domain.SubtopicPage.create_default_subtopic_page(
                self.subtopic_id_1, self.topic_id))
        self.subtopic_page_2 = (
            subtopic_page_domain.SubtopicPage.create_default_subtopic_page(
                self.subtopic_id_2, self.topic_id))
        subtopic_page_services.save_subtopic_page(
            self.admin_id, self.subtopic_page_1, 'Added subtopic', [
                topic_domain.TopicChange({
                    'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                    'subtopic_id': self.subtopic_id_1,
                    'title': 'Sample'
                })
            ])
        subtopic_page_services.save_subtopic_page(
            self.admin_id, self.subtopic_page_2, 'Added subtopic', [
                topic_domain.TopicChange({
                    'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                    'subtopic_id': self.subtopic_id_2,
                    'title': 'Sample'
                })
            ])
        subtopic_page_private_topic = (
            subtopic_page_domain.SubtopicPage.create_default_subtopic_page(
                self.subtopic_id_1, 'topic_id_2'))
        subtopic_page_services.save_subtopic_page(
            self.admin_id, subtopic_page_private_topic, 'Added subtopic', [
                topic_domain.TopicChange({
                    'cmd': topic_domain.CMD_ADD_SUBTOPIC,
                    'subtopic_id': self.subtopic_id_1,
                    'title': 'Sample'
                })
            ])
        subtopic = topic_domain.Subtopic.create_default_subtopic(
            1, 'Subtopic Title')
        subtopic.skill_ids = ['skill_id_1']
        subtopic.url_fragment = 'sub-url-frag-one'
        subtopic2 = topic_domain.Subtopic.create_default_subtopic(
            2, 'Subtopic Title 2')
        subtopic2.skill_ids = ['skill_id_2']
        subtopic2.url_fragment = 'sub-url-frag-two'

        self.save_new_topic(self.topic_id,
                            self.admin_id,
                            name='Name',
                            abbreviated_name='name',
                            url_fragment='name',
                            description='Description',
                            canonical_story_ids=[],
                            additional_story_ids=[],
                            uncategorized_skill_ids=[],
                            subtopics=[subtopic, subtopic2],
                            next_subtopic_id=3)
        topic_services.publish_topic(self.topic_id, self.admin_id)
        self.save_new_topic('topic_id_2',
                            self.admin_id,
                            name='Private_Name',
                            abbreviated_name='pvttopic',
                            url_fragment='pvttopic',
                            description='Description',
                            canonical_story_ids=[],
                            additional_story_ids=[],
                            uncategorized_skill_ids=[],
                            subtopics=[subtopic],
                            next_subtopic_id=2)
        self.recorded_voiceovers_dict = {
            'voiceovers_mapping': {
                'content': {
                    'en': {
                        'filename': 'test.mp3',
                        'file_size_bytes': 100,
                        'needs_update': False,
                        'duration_secs': 0.34234
                    }
                }
            }
        }
        self.written_translations_dict = {
            'translations_mapping': {
                'content': {}
            }
        }
        self.subtopic_page_1.update_page_contents_html(
            state_domain.SubtitledHtml.from_dict({
                'html': '<p>hello world</p>',
                'content_id': 'content'
            }))
        self.subtopic_page_1.update_page_contents_audio(
            state_domain.RecordedVoiceovers.from_dict(
                self.recorded_voiceovers_dict))
        subtopic_page_services.save_subtopic_page(
            self.admin_id, self.subtopic_page_1, 'Updated page contents', [
                subtopic_page_domain.SubtopicPageChange(
                    {
                        'cmd':
                        subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                        'subtopic_id': self.subtopic_id_1,
                        'property_name': 'page_contents_html',
                        'new_value': '<p>hello world</p>',
                        'old_value': ''
                    })
            ])
        self.subtopic_page_2.update_page_contents_html(
            state_domain.SubtitledHtml.from_dict({
                'html': '<p>hello world 2</p>',
                'content_id': 'content'
            }))
        self.subtopic_page_2.update_page_contents_audio(
            state_domain.RecordedVoiceovers.from_dict(
                self.recorded_voiceovers_dict))
        subtopic_page_services.save_subtopic_page(
            self.admin_id, self.subtopic_page_2, 'Updated page contents', [
                subtopic_page_domain.SubtopicPageChange(
                    {
                        'cmd':
                        subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY,
                        'subtopic_id': self.subtopic_id_2,
                        'property_name': 'page_contents_html',
                        'new_value': '<p>hello world 2</p>',
                        'old_value': ''
                    })
            ])