Search code examples
pythongoogle-drive-apigoogle-docs-apigoogle-api-python-clientgoogle-developers-console

Invalid table start location error while using gdoctableapp module to create table using google docs API in python


I'm trying to create a generic document using google docs API. I'm inserting images and dynamic tables whose borders are not visible.Tanaike's solution to create table by keeping borders invisible worked but here I'm appending all the requests in a single request then inserting into the document but I'm getting error after implementing the following code:

import io
from gdoctableapppy import gdoctableapp

SERVICE_FILENAME = 'C:/Users/XYZ/service_account.json'  # set path to service account filename

from googleapiclient.discovery import build
from google.oauth2 import service_account
from googleapiclient.http import MediaIoBaseDownload, MediaFileUpload

credentials = service_account.Credentials.from_service_account_file(SERVICE_FILENAME,
                                                                    scopes=['https://www.googleapis.com/auth/drive',
                                                                            'https://www.googleapis.com/auth/documents']
                                                                    )
docs = build('docs', 'v1', credentials=credentials)
drive = build('drive', 'v3', credentials=credentials)


def create_file(file_name):
    file_metadata = {
        "title": file_name,
        "body": {}
    }

    file = docs.documents().create(body=file_metadata).execute()
    print('File ID: %s' % file.get('documentId'))
    file_id = file.get('documentId')
    try:
        permission = {
            "role": "writer",
            "type": "user",
            'emailAddress': '[email protected]'
        }
        result = drive.permissions().create(fileId=file_id, body=permission).execute()
        print(result)
        return file_id
    except Exception as e:
        print('An error occurred:', e)
    return None


def insert_data(file_id):
    requests = []
    requests.append(insert_image())
    requests.append(insert_text(2, '\n'))
    requests.append(insert_text(3, 'text'))
    values = [['Name of the Client/Organization', 'XYZ'], ['Industry', 'Software']]
    requests.append(insert_table_data(file_id, values))

    result = docs.documents().batchUpdate(documentId=file_id, body={'requests': requests}).execute()


def insert_image():
    image_data = {
        'insertInlineImage': {
            'location': {
                'index': 1
            },
            'uri':
                'https://www.oberlo.com/media/1603970279-pexels-photo-3.jpg?fit=max&fm=jpg&w=1824',
            'objectSize': {
                'height': {
                    'magnitude': 350,
                    'unit': 'PT'
                },
                'width': {
                    'magnitude': 350,
                    'unit': 'PT'
                }
            }

        }
    }
    return image_data


def insert_text(index, text):
    text_data = {
        "insertText":
            {
                "text": text,
                "location":
                    {
                        "index": index
                    }
            }
    }

    return text_data


def insert_table_data(file_id, values):
    documentId = file_id
    resource = {
        "oauth2": credentials,
        "documentId": documentId,
        "rows": len(values),
        "columns": len(values[0]),
        "append": True,
        "values": values,
    }
    gdoctableapp.CreateTable(resource)
    resource = {
        "oauth2": credentials,
        "documentId": documentId,
    }
    res = gdoctableapp.GetTables(resource)
    obj = {"color": {"color": {}}, "dashStyle": "SOLID", "width": {"magnitude": 0, "unit": "PT"}}
    data = {
        "updateTableCellStyle": {
            "tableCellStyle": {
                "borderBottom": obj,
                "borderTop": obj,
                "borderLeft": obj,
                "borderRight": obj,
            },
            "tableStartLocation": {
                "index": res['tables'][-1]['tablePosition']['startIndex']
            },
            "fields": "borderBottom,borderTop,borderLeft,borderRight"
        }
    }
    # docs.documents().batchUpdate(documentId=documentId, body={'requests': requests}).execute()
    return data


def download_as_docx(file_id):
    results = drive.files().get(fileId=file_id, fields="id, name, mimeType, createdTime").execute()
    docMimeType = results['mimeType']
    mimeTypeMatchup = {
        "application/vnd.google-apps.document": {
            "exportType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docExt": "docx"
        }
    }
    exportMimeType = mimeTypeMatchup[docMimeType]['exportType']
    # docExt = mimeTypeMatchup[docMimeType]['docExt']
    docName = results['name']
    request = drive.files().export_media(fileId=file_id,
                                         mimeType=exportMimeType)  # Export formats : https://developers.google.com/drive/api/v3/ref-export-formats
    # fh = io.FileIO(docName + "." + docExt, mode='w')
    fh = io.FileIO(docName, mode='w')
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print("Download %d%%." % int(status.progress() * 100))


def download_as_pdf(file_id, file_name):
    request = drive.files().export_media(fileId=file_id,
                                         mimeType='application/pdf')
    fh = io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print("Download %d%%." % int(status.progress() * 100))
    fh.seek(0)
    filename = file_name.split('.docx')[0] + '.pdf'
    with open(filename, 'wb') as fx:
        fx.write(fh.getvalue())


def delete_gdrive_file(file_id):
    """Deleted file on Google Drive
    :param file_id: ID of Google Drive file
    """
    response = drive.files().delete(fileId=file_id).execute()
    print(response)


if __name__ == '__main__':
    file_name = 'Data.docx'
    file_id = create_file(file_name)
    insert_data(file_id)
    download_as_docx(file_id)
    download_as_pdf(file_id, file_name)
    delete_gdrive_file(file_id)

Error:

googleapiclient.errors.HttpError: <HttpError 400 when requesting https://docs.googleapis.com/v1/documents/1Qsf3CRKiIS9Ayjws1GEhg5oGiZbrTBAObMsifxdfre:b atchUpdate?alt=json returned "Invalid requests[3].updateTableCellStyle: The provided table start location is invalid.". Details: "Invalid requests[3].up dateTableCellStyle: The provided table start location is invalid.">


Solution

  • When I saw your script after the table was appended by gdoctableapp, the text and image are inserted from the top of the document, and the request for removing the borders is run. By this, the index of the appended table is different. So such an error occurs. I thought that this is the reason for your issue. In order to remove this issue, how about the following modification?

    From:

    def insert_data(file_id):
        requests = []
        requests.append(insert_image())
        requests.append(insert_text(2, '\n'))
        requests.append(insert_text(3, 'text'))
        values = [['Name of the Client/Organization', 'XYZ'], ['Industry', 'Software']]
        requests.append(insert_table_data(file_id, values))
    
        result = docs.documents().batchUpdate(documentId=file_id, body={'requests': requests}).execute()
    

    To:

    def insert_data(file_id):
        requests = []
        values = [['Name of the Client/Organization', 'XYZ'], ['Industry', 'Software']]
        requests.append(insert_table_data(file_id, values))
        requests.append(insert_image())
        requests.append(insert_text(2, '\n'))
        requests.append(insert_text(3, 'text'))
    
        result = docs.documents().batchUpdate(documentId=file_id, body={'requests': requests}).execute()