Search code examples
pythongoogle-docsgoogle-docs-api

How to write a list of mixed type of nested elements in Google document using Google Docs API (Python)?


My first post here. My apologies in advance if I inadvertently break any rules.

Prerequisites:

Assume that I've created a Google service account, and obtained the credentials (JSON) file. I created a Google document, and I've its doc_ID available. My programming language is Python.

Problem:

Assume that I've a list of mixed-type of elements, e.g. (normal text, few nested list items, normal text, another nested list item), etc. See the example below:

my_list = [
    "This is the normal text line, without any itemized list format.",
    "* This is the outermost list item (number)",
    "* This is another outermost list item (number)",
    "  * This is a first-level indented list item (alpha)",
    "    * This is a second-level indented list item (roman)",
    "    * This is another second-level indented list item (roman)",
    "  * This is another first-level indented list item (alpha)",
    "This is the normal text line, without any itemized list format.",
    "* This is one more outermost list item (number)",
]

I want to write it to a Google document and obtain a mix of normal text paragraph, followed by a nested and properly indented numbered list, followed by a line of normal text paragraph, and finally, another nested list item (preferably continuing from my previous preset, but with the fresh numbering will also be acceptable).

I tried several approaches, and got the closest possible result with the suggestion of @tanaike in the following post. How do I indent a bulleted list with the Google Docs API

Here is my sample code:

    text_insert = ""
    for text in my_list:
        if text.startswith('* '):
            text_insert += text[2:] + '\n'
        elif text.startswith('  * '):
            text_insert += '\t' + text[4:] + '\n'
        elif text.startswith('    * '):
            text_insert += '\t\t' + text[6:] + '\n'
        else:
            text_normal = text + '\n'
            text_insert += text_normal
            end_index_normal = start_index + len(text_insert) + 1 - indent_corr
            start_index_normal = end_index_normal - len(text_normal)

    end_index = start_index + len(text_insert) + 1

    indented_requests = [
        {
            "insertText": {
                "text": text_insert,
                'location': {'index': start_index},
            }
        },
        {
            "createParagraphBullets": {
                'range': {
                    'startIndex': start_index,
                    'endIndex': end_index,  # Add 2 for the newline character
                },
                "bulletPreset": "NUMBERED_DECIMAL_ALPHA_ROMAN",
            }
        },
        {
            "deleteParagraphBullets": {
                'range': {
                    'startIndex': start_index_normal,
                    'endIndex': end_index_normal,
                },
            }
        },
    ]

    try:
        u_service.documents().batchUpdate(documentId=doc_id, body={'requests': indented_requests}).execute()
    except HttpError as error:
        print(f"An error occurred: {error}")

What I get in my Google document is the following:

enter image description here

However, my goal is the following (obtained after manual editing):

enter image description here

How can I achieve this? Any help will be greatly appreciated.


Solution

  • Modification points:

    • In your showing script, end_index_normal and start_index_normal are overwritten by another loop.
    • In the case of deleteParagraphBullets, only one character is selected.
    • Also, I guessed that in your situation, the last character might not be required to be included in createParagraphBullets.

    When these points are reflected in your script, how about the following modification?

    Modified script:

    # Please set your variables.
    doc_id = "###"
    start_index = 1
    indent_corr = 1
    
    
    my_list = [
        "This is the normal text line, without any itemized list format.",
        "* This is the outermost list item (number)",
        "* This is another outermost list item (number)",
        "  * This is a first-level indented list item (alpha)",
        "    * This is a second-level indented list item (roman)",
        "    * This is another second-level indented list item (roman)",
        "  * This is another first-level indented list item (alpha)",
        "This is the normal text line, without any itemized list format.",
        "* This is one more outermost list item (number)",
    ]
    text_insert = ""
    deleteParagraphBullets = []
    for text in my_list:
        if text.startswith('* '):
            text_insert += text[2:] + '\n'
        elif text.startswith('  * '):
            text_insert += '\t' + text[4:] + '\n'
        elif text.startswith('    * '):
            text_insert += '\t\t' + text[6:] + '\n'
        else:
            text_normal = text + '\n'
            text_insert += text_normal
            end_index_normal = start_index + len(text_insert) + 1 - indent_corr
            start_index_normal = end_index_normal - len(text_normal)
            deleteParagraphBullets.append({
                "deleteParagraphBullets": {
                    'range': {
                        'startIndex': start_index_normal,
                        'endIndex': start_index_normal + 1,
                    },
                }
            })
    deleteParagraphBullets.reverse()
    indented_requests = [
        {
            "insertText": {
                "text": text_insert,
                'location': {'index': start_index},
            }
        },
        {
            "createParagraphBullets": {
                'range': {
                    'startIndex': start_index,
                    'endIndex': start_index + len(text_insert),
                },
                "bulletPreset": "NUMBERED_DECIMAL_ALPHA_ROMAN",
            }
        }
    ] + deleteParagraphBullets
    u_service.documents().batchUpdate(documentId=doc_id, body={'requests': indented_requests}).execute()
    

    Testing:

    When this script is run, the following result is obtained.

    From

    enter image description here

    To

    enter image description here

    References: