Cannot Interact with Shared Drives & Files | Python Google Drive API v3 - python

I'm working on a python script that would manage a file on a shared drive using Google Drive API v3. However, when I try to download or replace that file, I get the following error:
An error occurred: <HttpError 404 when requesting https://www.googleapis.com/upload/drive/v3/files/1HhCxshcR17I4rM0gtBIvHzEH_jzd7nV2?alt=json&uploadType=multipart returned "File not found: 1HhCxshcR17I4rM0gtBIvHzEH_jzd7nV2.">
Here is my code
def update_file(service, file_id, new_title, new_description, new_mime_type,
new_filename):
"""Update an existing file's metadata and content.
Args:
service: Drive API service instance.
file_id: ID of the file to update.
new_title: New title for the file.
new_description: New description for the file.
new_mime_type: New MIME type for the file.
new_filename: Filename of the new content to upload.
new_revision: Whether or not to create a new revision for this file.
Returns:
Updated file metadata if successful, None otherwise.
"""
try:
# File metadata
file_metadata = {
'name': new_title,
'description': new_description
}
# File's new content.
media_body = MediaFileUpload(
new_filename, mimetype=new_mime_type) # In this case the file is small so no resumable flag needed
# Send the request to the API.
updated_file = service.files().update(
fileId=file_id,
body=file_metadata,
media_body=media_body).execute()
return updated_file
except Exception as e:
print ('An error occurred: %s' % e)
return None
#Update an existing entry in database
def updateItem(dataIndex):
userInput = input(header[2]) #Update quantity
if userInput[0] == '+':
ws['C' + str(dataIndex)] = str( int(ws['C' + str(dataIndex)].value) + int(userInput[1:]) )
elif userInput[0] == '-':
ws['C' + str(dataIndex)] = str( int(ws['C' + str(dataIndex)].value) - int(userInput[1:]) )
else:
ws['C' + str(dataIndex)] = str( userInput )
ws['D' + str(dataIndex)] = today #Update date
wb.save(filepath)
How do I use Google Drive API on a shared drive or document? This works fine in my personal drive.

If you want to use the file of file_id in your shared Drive with files().update() in Drive API v3, how about the following modification?
Modified script:
updated_file = service.files().update(
fileId=file_id,
body=file_metadata,
media_body=media_body,
supportsAllDrives=True # <--- Added
).execute()
The default value of supportsAllDrives is false. So in this case, true is used for using the file in the shared Drive.
Reference:
Files: update

Related

Reading files from G Drive from python

I have a GCP project and there is a Jupiter notebook which is to read files from G Drive.
def search_file(request = None):
try:
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=1, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
return
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
except HttpError as error:
print(f'An error occurred: {error}')
I'm using Google's quickstart to retrieve files from G Drive. The authentication and all works fine and the intended drive has just one file on it. But when I execute this search_file method, I'm getting No Files found. message. I'm trying to read one csv file which resides in G Drive. What I have missed or any other alternatives for this?
Can you try running the query in the following format?
shared_drive_id = "" # you need to specify specific id of drive folder provided in url of that folder such as https://drive.google.com/drive/folders/<shared_drive_id>
query_file = "mimeType='application/vnd.google-apps.document' and trashed=false"
search_file = drive_service.files().list(q=query_file,
fields='files(id)',
driveId=shared_drive_id,
supportsAllDrives=True,
includeItemsFromAllDrives=True,
corpora='drive').execute()
files = search_file.get('files', [])

How to rename Google Drive file with Python

how are you doing?
I'm trying to rename some files in my Google Drive folder, following the instructions listed in documentation.
However I'm getting an error in one of the arguments (FileNotFoundError: [Errno 2] No such file or directory: 'trying_new_filename_string')
I ONLY NEED TO RENAME THE DOCUMENT, NOTHING ELSE.
This is my code:
service = build('drive', 'v3', credentials=credentials)
file_id = '1TKxcYDzEK3SUjSv6dCMM6WkKRmZwm84SPWVhR1F2DEc'
new_title = 'trying_new_title'
new_mime_type = 'trying_new_mime_type'
new_filename = 'trying_new_filename_string'
from apiclient import errors
from apiclient.http import MediaFileUpload
def update_file(service, file_id, new_title, new_mime_type, new_filename):
# First retrieve the file from the API.
file = service.files().get(fileId=file_id).execute()
# File's new metadata.
file['name'] = new_title
file['mimeType'] = new_mime_type
# File's new content.
media_body = MediaFileUpload(
new_filename, mimetype=new_mime_type, resumable=True)
# Send the request to the API.
updated_file = service.files().update(
fileId=file_id,
body=file,
media_body=media_body).execute()
return updated_file
Seems related to the fact the new_filename does not exist. As the MediaUploadFile tries to open and does not find a file with the trying_new_filename_string name.
As you only need to rename that file, don't change its content, you should remove the MediaUploadFile method.
Try:
def renameFile(service, fileId, newTitle):
body = {'name': newTitle}
return service.files().update(fileId=fileId, body=body).execute()

Google Drive create new folder in shared drive

I'm able to create a new folder within a parent folder on my personal Google Drive, but when I try to do it in a shared drive, I get this error:
<HttpError 404 when requesting https://www.googleapis.com/drive/v3/files?fields=id&alt=json returned "File not found:
It looks like a similar problem from this question, which wasn't resolved.
I'm a manager on the account, and other commands, such as creating new files, work fine.
This is the function I'm successfully using when writing to my personal Drive:
def create_folder_in_folder(folder_name,parent_folder_id):
file_metadata = {
'name' : folder_name,
'parents' : [parent_folder_id],
'mimeType' : 'application/vnd.google-apps.folder'
}
file = service.files().create(body=file_metadata,
fields='id').execute()
print ('Folder ID: %s' % file.get('id'))
How about this modification?
From:
file = service.files().create(body=file_metadata, fields='id').execute()
To:
file = service.files().create(body=file_metadata, supportsAllDrives=True, fields='id').execute()
supportsAllDrives=True is added.
I think that the reason of the error message is due to this.
Note:
In this case, it supposes that you have the permission for creating the folder to the shared Drive.
Reference:
Files: create

How to make a link to S3 file download

I want to make a linke to download S3 stored file.
<a href="https://s3.region.amazonaws.com/bucket/file.txt" download>DownLoad</a>
it only display file.txt on the browser.
So I found way to download. It is add Content-Disposition : attachment meta tag to file.
But I need to add this meta tag to new file automately. So I made lambda function by python.
import json
import urllib.parse
import boto3
print('Loading function')
s3 = boto3.client('s3')
def lambda_handler(event, context):
#print("Received event: " + json.dumps(event, indent=2))
# Get the object from the event and show its content type
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
try:
response = s3.get_object(Bucket=bucket, Key=key)
print("CONTENT TYPE: " + response['ContentType'])
except Exception as e:
print(e)
print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
raise e
try:
s3_2 = boto3.resource('s3')
s3_object = s3_2.Object(bucket, key)
print(s3_object.metadata)
s3_object.metadata.update({'ContentDisposition':'attachment'})
print(bucket, key)
s3_object.copy_from(CopySource={'Bucket':bucket, 'Key':key}, Metadata=s3_object.metadata, MetadataDirective='REPLACE')
except:
print(s3_object.metadata)
return response['ContentType']
But this function add user defined metatag not system metatag. . .
What should I do?
Content-Disposition is treated by S3 as (somewhat) more like system metadata than custom/user-defined metadata, so it has its own argument.
s3_object.copy_from(CopySource={'Bucket':bucket, 'Key':key}, ContentDisposition='attachment', Metadata=s3_object.metadata, MetadataDirective='REPLACE')
Note that you still need Metadata and MetadataDirective as shown, for this to work, but s3_object.metadata.update() is not required since you are not changing the custom metadata.

python + google drive: upload xlsx, convert to google sheet, get sharable link

The flow of my desired program is:
Upload an xlsx spreadsheet to drive (it was created using pandas to_excel)
Convert it to Google Sheets format
Specify that it is editable by anyone with the link
Get the link and share it with someone who will enter information
Download the completed sheet
I am currently using PyDrive, which solves steps 1 and 5, but there are a few unsolved problems.
How can I convert to google sheets format? I tried to just specify the mimeType as 'application/vnd.google-apps.spreadsheet' when I created the file to upload with PyDrive, but that gave me an error.
How can I set the file to be editable by anyone with the link? Once that is set, I can get the sharing link easily enough with PyDrive.
UPDATE: conversion from xlsx to google sheets is easy with a convert=True flag. See below. I am still seeking a way to set the sharing settings of my new file to "anyone with the link can edit".
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
gauth = GoogleAuth()
gauth.LocalWebserverAuth()
drive = GoogleDrive(gauth)
test_file = drive.CreateFile({'title': 'testfile.xlsx'})
test_file.SetContentFile('testfile.xlsx')
test_file.Upload({'convert': True})
There is an Optional query parameter of "convert", for both the "INSERT" and "COPY" method;
convert=true,
Whether to convert this file to the corresponding Google Docs format. (Default: false)
There is a python example here:
Google Documentation - Copy
You need to use the Python client library for the code to work.
from apiclient import errors
from apiclient.http import MediaFileUpload
# ...
def insert_file(service, title, description, parent_id, mime_type, filename):
"""Insert new file.
Args:
service: Drive API service instance.
title: Title of the file to insert, including the extension.
description: Description of the file to insert.
parent_id: Parent folder's ID.
mime_type: MIME type of the file to insert.
filename: Filename of the file to insert.
Returns:
Inserted file metadata if successful, None otherwise.
"""
media_body = MediaFileUpload(filename, mimetype=mime_type, resumable=True)
body = {
'title': title,
'description': description,
'mimeType': mime_type
}
# Set the parent folder.
if parent_id:
body['parents'] = [{'id': parent_id}]
try:
file = service.files().insert(
body=body,
convert=true,
media_body=media_body).execute()
# Uncomment the following line to print the File ID
# print 'File ID: %s' % file['id']
return file
except errors.HttpError, error:
print 'An error occured: %s' % error
return None
I haven't tried this, so you'll need to test it.
In order to set the file to be editable for anyone with the link , you have to insert a new permission with the following information:
from apiclient import errors
# ...
def share_with_anyone(service, file_id):
"""Shares the file with anyone with the link
Args:
service: Drive API service instance.
file_id: ID of the file to insert permission for.
Returns:
The inserted permission if successful, None otherwise.
"""
new_permission = {
'type': "anyone",
'role': "writer",
'withLink': True
}
try:
return service.permissions().insert(
fileId=file_id, body=new_permission).execute()
except errors.HttpError, error:
print 'An error occurred: %s' % error
return None
then to get the link you go to : file["alternateLink"]

Categories