0

I want to copy an existing template ppt present in my google drive. Then I want to change the placeholder text to some other text.

here is what I am trying.

from google.oauth2 import service_account
import googleapiclient.discovery


SCOPES = (
    'https://www.googleapis.com/auth/drive',
    'https://www.googleapis.com/auth/presentations',
)
SERVICE_ACCOUNT_FILE = 'cred.json'

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES) 

SLIDES = discovery.build('slides', 'v1', credentials=credentials)
DRIVE  = discovery.build('drive',  'v3', credentials=credentials)

TMPLFILE = 'title slide template' 

rsp = DRIVE.files().list(q="name='%s'" % TMPLFILE).execute().get('files')[0]
print(rsp)
DATA = {'name': 'Google Slides API template DEMO'}
print('** Copying template %r as %r' % (rsp['name'], DATA['name']))
DECK_ID = DRIVE.files().copy(body=DATA, fileId=rsp['id']).execute().get('id')
print(DECK_ID)

print('** Replacing placeholder text')

reqs = [
    {'replaceAllText': {
        'containsText': {'text': '{{text}}'},
        'replaceText': final_til[0]
    }},
]

SLIDES.presentations().batchUpdate(body={'requests': reqs},
        presentationId=DECK_ID).execute()
print('DONE')

But it is not working. I don't get any error. everything works fine but I don't see the new ppt.

Output:

{'kind': 'drive#file', 'id': '15mVjkrT7PkckKetK_q9aYRVxaDcwDdHpAh7xjrAWB6Q', 'name': 'title slide template', 'mimeType': 'application/vnd.google-apps.presentation'}  <--- rsp
** Copying template 'title slide template' as 'Google Slides API template DEMO'
11O97tySSNaboW6YRVD62Q7HLs8aVuS2pWyLYXImdSec   <-- DECK_ID
** Replacing placeholder text
DONE

If I change

SLIDES.presentations().batchUpdate(body={'requests': reqs},
            presentationId=DECK_ID).execute()

to

SLIDES.presentations().batchUpdate(body={'requests': reqs},
            presentationId=rsp.get('id')).execute()

then it does replace the text but in my template file which I don't want.

Why is this happening?

Tanaike
  • 181,128
  • 11
  • 97
  • 165
ray an
  • 1,132
  • 3
  • 17
  • 42

2 Answers2

2

I believe your current situation and goal as follows.

  • From your script,
    • You are using googleapis for python.
    • You have already been able to use Drive API and Slides API using the service account.
    • an existing template ppt present is a Google Slides which is not the PowerPoint file.

Modification points:

  • From your script and But it is not working. I don't get any error. everything works fine but I don't see the new ppt., I understood that you might want to see the Google Slides copied by the service account at your Google Drive.

    • When the Google Slides is copied by the service account, the copied Google Slides is put to the Drive of the service account. The Drive of service account is different from your Google Drive. By this, you cannot see the copied Google Slides on your Drive. I thought that this might be the reason of your issue.
  • In order to see the Google Slides copied by the service account at your Google Drive, for example, the following workarounds can be used.

    1. Share the copied Google Slides with your email of Google account.
      • In this case, you can see the shared file at the shared folder.
    2. At first, it creates new folder in your Google Drive and share the folder with the email of the service account. And when the Google Slides is copied, it sets the shared folder as the destination folder.

Workaround 1:

In this workaround, it shares the copied Google Slides with your email of Google account. When this is reflected to your script, it becomes as follows.

Modified script:

In this case, new permission is created to the copied file using "Permissions: create".

From:
print(DECK_ID)

print('** Replacing placeholder text')
To:
print(DECK_ID)

permission = {
    'type': 'user',
    'role': 'writer',
    'emailAddress': '###@gmail.com'  # <--- Please set the email of your Google account.
}
DRIVE.permissions().create(fileId=DECK_ID, body=permission).execute()

print('** Replacing placeholder text')

Workaround 2:

In this workaround, the Google Slides is copied to the shared folder in your Google Drive. Before you use this script, please create new folder and share the folder with the email of service account. When this is reflected to your script, it becomes as follows.

Modified script:

In this case, the metadata is added to the request body of DRIVE.files().copy().

From:
DATA = {'name': 'Google Slides API template DEMO'}
To:
DATA = {'name': 'Google Slides API template DEMO', 'parents': ['###']}
  • Please set the folder ID of shared folder to ###.

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • hey, thanks for the answer. I have another question though. Is it possible to convert this new presentation to a png file and save that in the drive instead of the presentation?(this presentation has only 1 slide) – ray an Nov 01 '20 at 22:10
  • @rayan Thank you for replying. I'm glad your issue was resolved. I would like to support you. But the issue of your comment is new issue, and that is different from your question. So can you post it as new question by including the detail information? Because when your initial question is changed by comment, other users who see your question are confused. By posting it as new question, users including me can think of your new question. If you can cooperate to resolve your new issue, I'm glad. Can you cooperate to resolve your new question? – Tanaike Nov 01 '20 at 22:55
  • sure. https://stackoverflow.com/questions/64644254/export-a-google-slide-as-an-image-via-google-slides-api-or-drive-api – ray an Nov 02 '20 at 10:59
  • @rayan Thank you for your response. I saw it now. I noticed that an answer has already been posted. I would like to respect the existing answer. I think that it will resolve your issue. – Tanaike Nov 02 '20 at 23:15
  • @Tanaike you're SO AMAZING. You saved my day. I have been scratching my head over this service-account permissions issue for 2 weeks without any resolution. This finally worked!!! I wish google had made it easier. Couple of followup questions for you: 1) is workaround 1 or 2 more preferable? 2) does the google service-account have a storage limit? what if my program keeps creating presentations there and I lose track of it? Should I programmatically delete the old presentations? – user3422637 Jun 06 '22 at 03:51
  • @user3422637 Thank you for your comment. I'm glad your issue was resolved. About your 4 questions, I would like to support you. About the answer to your 1st question, I think that both patterns can be used. About the answer to your 2nd question, it's yes. About the answer to your 3rd question, I cannot understand this. About the answer to your 4th question, I cannot understand this. The reason that I cannot answer for your all 4 questions is due to my poor English skill. I deeply apologize for this. I would like to study more. – Tanaike Jun 06 '22 at 05:39
1

@Tanaike's answer is great, but there is one other option too:

Account Impersonation

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)
        
delegated_credentials = credentials.with_subject(<email>)

DRIVE = build('drive','v3', credentials = delegated_credentials)

Here is a good overview: Using OAuth 2.0 for Server to Server Applications, specifically this section goes through the code.

Remember to set Domain Wide Delegation in both the GCP console and the Admin console.

The project initialized in the GCP Cloud console has also been granted scopes from within the Admin console > Security > API Controls > Domain wide delegation > Add new

The first thing the script does is build the credentials using from_service_account_file:

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

Then it builds the delegated credentials, that is, the user to be impersonated:

delegated_credentials = credentials.with_subject('<EMAIL>')

From there it can build the service as normal. You can save to the user's drive as if it were the user doing it themselves.

References

iansedano
  • 6,169
  • 2
  • 12
  • 24