-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdrive.py
351 lines (256 loc) · 8.28 KB
/
drive.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# -*- coding: utf-8 -*-
"""
Google Drive API
Tue 13 Sep 22:16:41 2016
"""
import uuid
import logging
from google_objects.core import GoogleClient
from google_objects.core import GoogleObject
log = logging.getLogger(__name__)
class DriveClient(GoogleClient):
"""Google Drive Wrapper Object,
exposes all Drive API operations.
callback: receving webook URL.
"""
service = 'drive'
version = 'v3'
scope = {'drive'}
def get_about(self, fields=['user']):
data = self.resource.about().get(
fields=', '.join(fields)
).execute()
return About.from_existing(data)
def get_file(self, file_id):
"""Returns an initialized
File Instance.
:file_id: Google Drive File ID
:returns: <File>
"""
data = self.resource.files().get(
fileId=file_id
).execute()
return File.from_existing(data, self)
def copy_file(self, file_id, file_body=None):
"""Copy file and place in folder.
:file_id: drive file id
:folder_id: drive file#folder id
:returns: new, copied <File>
"""
# get old file metadata if none provided
if not file_body:
file_body = self.resource.files().get(
fileId=file_id
).execute()
new_file = self.resource.files().copy(
fileId=file_id,
body=file_body,
fields='id, webViewLink'
).execute()
return File.from_existing(new_file, self)
def list_files(self, file_type=None,
parents=[], fields=['files(id, name)']):
"""Shows basic usage of the Google Drive API.
Creates a Google Drive API service object and outputs the names and IDs
for up to 10 files.
"""
if hasattr(fields, '__iter__'):
fields = ', '.join(fields)
query = ''
if file_type:
prfx = 'application/vnd.google-apps.'
query = query + "mimeType='{}'".format('application/vnd.google-apps.' + file_type.lower())
for p in parents:
query = query + ' and \'{}\' in parents'.format(p)
result = self.resource.files().list(
q=query, pageSize=100, # fields=fields
).execute()
files = result.get('files')
return [File.from_existing(each, self) for each in files]
def watch_file(self, file_id,
channel_id=None, callback=None, type='webhook'):
"""Commences push notifications for a file resource,
depends on callback url being set on instance.
:file_id: Google Drive File Resource ID
:returns:
"""
if not (callback or self.callback):
raise ValueError('Callback URL required to watch resources.')
req_body = {
'id': channel_id or str(uuid.uuid4()),
'type': type,
'address': callback or self.callback
}
resp = self.resource.files().watch(
fileId=file_id, body=req_body
).execute()
return resp
def create_permission(self, file_id,
permission, message=None, notification=True):
# makes api call
data = self.resource.permissions().create(
fileId=file_id,
body=permission,
emailMessage=message,
sendNotificationEmail=notification,
).execute()
return Permission(**data)
class About(GoogleObject):
"""Docstring for User Resource, this is READ ONLY"""
@property
def user(self):
return self.data['user']
@property
def email(self):
return self.user['emailAddress']
@property
def name(self):
return self.user['displayName']
@property
def photo(self):
return self.user['photoLink']
@property
def permission_id(self):
return self.user['permissionId']
class File(GoogleObject):
"""Represents a Google Drive File Resource"""
_type_prefix = 'application/vnd.google-apps.'
# drive file types
_default_type = 'unknown'
_types = {
'audio',
'document',
'drawing',
'file',
'folder',
'form',
'fusiontable',
'map',
'photo',
'presentation',
'script',
'sites',
'spreadsheet',
'unknown',
'video',
}
def __init__(self, client=None, **kwargs):
"""Initialize File Object
:data: <Dict> of file data
:client: <DriveClient>
"""
self.client = client
self.__new_permissions = []
self.__updates = []
# initalize the other properties
super().__init__(**kwargs)
@property
def id(self):
return self.data['id'] or None
@property
def name(self):
return self.data['name'] or None
@name.setter
def name(self, val):
self.data['name'] = val
@property
def url(self):
return self.data['webViewLink']
@property
def type_prefix(self):
return self.data['typePrefix']
@property
def type(self):
return self.data['mimeType']
@type.setter
def type(self, value):
"""ensures type is valid, assigns type to
unknown if no argument is given
"""
file_type = value or self._default_type
file_type = value.lower()
if value not in self._types:
raise ValueError
self.data['mimeType'] = '{}{}'.format(self.type_prefix, file_type)
@property
def parents(self):
return self.data['parents']
@parents.setter
def parents(self, value):
self.data['parents'] = value
def copy(self, name=None, parents=[]):
"""Copies self, optionally altering
name and parent folders.
"""
new = dict()
new['name'] = name or '{0} | COPY'.format(self.name)
if parents:
new['parents'] = parents
return self.client.copy_file(self.id, new)
def permissions(self):
return [Permission(self, **each) for each in self.data['permissions']]
def add_permission(self, email, **kwargs):
"""initializes new permission objects and
pushes it, returns
and adds it
to queue
"""
kwargs.update({'email': email, 'emailAddress': email})
permission = Permission.from_existing(kwargs, self)
message = kwargs.get('message')
notification = kwargs.get('notification')
created = self.client.create_permission(
self.id, permission.serialize(), message, notification
)
created.file = self
created.email = email
return created
def watch(self, **kwargs):
"""Attempts to start receiving push notifications for this file.
:channel_id: UUID
:returns: Dictionary detailing watch request.
"""
return self.client.watch_file(self.id, **kwargs)
class Permission(GoogleObject):
"""Google Drive File Permission"""
_default_role = 'reader'
_role_levels = {'reader', 'commenter', 'writer', 'owner'}
_default_type = 'user'
_type_levels = {'user', 'group', 'domain', 'anyone'}
# api key names
_properties = {'role', 'type',
'emailAddress', 'allowFileDiscovery', 'domain'}
# constructors
def __init__(self, file=None, **kwargs):
"""Initialize Permission Object
:data: <Dict> of Permission file data
:level: one of reader, commenter, writer, or owner
"""
self.file = file
super().__init__(**kwargs)
@property
def id(self):
return self.data['id']
@property
def role(self):
return self.data.get('role', self._default_role)
@role.setter
def role(self, value):
if value in self._role_levels:
self.data['role'] = value
@property
def type(self):
return self.data.get('type', self._default_type)
@type.setter
def type(self, value):
if value in self._type_levels:
self.data['type'] = value
@property
def email(self):
return self.data['emailAddress']
@email.setter
def email(self, value):
# TODO:
# add update call if _id is present
if len(value.split('@')) == 2:
self.data['emailAddress'] = value