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:

    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: