Example #1
0
 def test_get_subtopic_pages_with_ids(self):
     subtopic_ids = [self.subtopic_id]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(
         subtopic_pages[0].to_dict(), self.subtopic_page.to_dict())
     subtopic_ids = [2]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(subtopic_pages, [None])
     subtopic_ids = [self.subtopic_id, 2]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     expected_subtopic_pages = [self.subtopic_page.to_dict(), None]
     self.assertEqual(
         [subtopic_pages[0].to_dict(), subtopic_pages[1]],
         expected_subtopic_pages)
     subtopic_ids = []
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(subtopic_pages, [])
     subtopic_ids = [2, 2]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(subtopic_pages, [None, None])
 def test_get_subtopic_pages_with_ids(self) -> None:
     subtopic_ids = [self.subtopic_id]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     # Ruling out the possibility of None for mypy type checking.
     assert subtopic_pages[0] is not None
     self.assertEqual(subtopic_pages[0].to_dict(),
                      self.subtopic_page.to_dict())
     subtopic_ids = [2]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(subtopic_pages, [None])
     subtopic_ids = [self.subtopic_id, 2]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     expected_subtopic_pages = [self.subtopic_page.to_dict(), None]
     # Ruling out the possibility of None for mypy type checking.
     assert subtopic_pages[0] is not None
     self.assertEqual([subtopic_pages[0].to_dict(), subtopic_pages[1]],
                      expected_subtopic_pages)
     subtopic_ids = []
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(subtopic_pages, [])
     subtopic_ids = [2, 2]
     subtopic_pages = subtopic_page_services.get_subtopic_pages_with_ids(
         self.TOPIC_ID, subtopic_ids)
     self.assertEqual(subtopic_pages, [None, None])
Example #3
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()
Example #4
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). 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 and a list of ids of the newly
            created subtopics.
    """
    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 = {}

    for change in change_list:
        if (change.cmd ==
                subtopic_page_domain.CMD_UPDATE_SUBTOPIC_PAGE_PROPERTY):
            if change.id < topic.next_subtopic_id:
                existing_subtopic_page_ids_to_be_modified.append(change.id)
    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))
                newly_created_subtopic_ids.append(change.subtopic_id)
            elif change.cmd == topic_domain.CMD_DELETE_SUBTOPIC:
                topic.delete_subtopic(change.id)
                if change.id in newly_created_subtopic_ids:
                    raise Exception('The incoming changelist had simultaneous'
                                    ' creation and deletion of subtopics.')
                deleted_subtopic_ids.append(change.id)
            elif change.cmd == topic_domain.CMD_ADD_UNCATEGORIZED_SKILL_ID:
                topic.add_uncategorized_skill_id(change.id)
            elif change.cmd == topic_domain.CMD_REMOVE_UNCATEGORIZED_SKILL_ID:
                topic.remove_uncategorized_skill_id(change.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)
                else:
                    raise Exception('Invalid change dict.')
            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.id))
                if ((modified_subtopic_pages[subtopic_page_id] is None)
                        or (change.id in deleted_subtopic_ids)):
                    raise Exception('The subtopic with id %s doesn\'t exist' %
                                    change.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)
                else:
                    raise Exception('Invalid change dict.')
            elif change.cmd == topic_domain.CMD_UPDATE_SUBTOPIC_PROPERTY:
                if (change.property_name ==
                        topic_domain.SUBTOPIC_PROPERTY_TITLE):
                    topic.update_subtopic_title(change.id, change.new_value)
                else:
                    raise Exception('Invalid change dict.')
            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
            else:
                raise Exception('Invalid change dict.')
        return (topic, modified_subtopic_pages, deleted_subtopic_ids,
                newly_created_subtopic_ids)

    except Exception as e:
        logging.error('%s %s %s %s' %
                      (e.__class__.__name__, e, topic_id, change_list))
        raise