Skip to content

Commit 1246410

Browse files
committed
convert dateTime values to datetime instances
1 parent 14f9f8e commit 1246410

File tree

9 files changed

+169
-136
lines changed

9 files changed

+169
-136
lines changed

examples/files.py

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,69 @@
11
import os
22
from msgraph import api, user, files
33

4-
authority_host_uri = 'https://login.microsoftonline.com'
5-
tenant = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
6-
resource_uri = 'https://graph.microsoft.com'
7-
client_id = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
8-
client_thumbprint = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
9-
client_certificate_path = os.path.join('data', 'files.pem')
10-
with open(client_certificate_path, 'rb') as input_file:
11-
client_certificate = input_file.read()
12-
api_instance = api.GraphAPI.from_certificate(authority_host_uri, tenant, resource_uri, client_id, client_certificate, client_thumbprint)
13-
14-
test_user = user.User.get(api_instance, '[email protected]')
15-
# fetch a OneDrive of a given user
16-
drive = files.Drive.get(api_instance, user=test_user)
17-
# fetch the OneDrive for a given site
18-
# drive = files.Drive.get(api_instance, site='a258178f-da15-42dd-a85b-90dbe49ebd9e')
19-
20-
# fetch drives accessible by a given user
21-
accessible_drives = files.Drive.accessible(api_instance, user='[email protected]')
22-
print(accessible_drives)
23-
24-
# fetch the root folder of the drive
25-
drive_root_folder = files.DriveItem.root_folder(api_instance, drive=drive)
26-
27-
# create a new folder in the root folder
28-
new_folder = files.DriveItem.create_folder(api_instance, "Test", drive_root_folder, drive=drive)
29-
# create a file in the new folder
30-
new_file = files.DriveItem.upload(api_instance, 'Hello, World!', drive=drive, parent=new_folder, file_name='This is a test.txt')
31-
32-
# overwrite the file in the new folder
33-
overwriting_file = files.DriveItem.upload(api_instance, 'Hello, John!', drive=drive, parent_id=new_folder, item=new_file, replace=True)
34-
35-
# update the file name of the file
36-
overwriting_file.name = 'My new file name.txt'
37-
overwriting_file.update(api_instance, drive=drive)
38-
# or alternatively
39-
overwriting_file.move(api_instance, 'My new file name.txt', folder=drive_root_folder, drive=drive)
40-
41-
# search for .txt files on the drive
42-
search_results = files.DriveItem.search(api_instance, '.txt', drive=drive)
43-
# delete the created file
44-
overwriting_file.delete(api_instance, drive=drive)
45-
46-
# fetch children of the root directory of a drive
47-
root_children = files.DriveItem.get_children(api_instance, drive=drive)
48-
# fetch children of parent folder
49-
folder_children = files.DriveItem.get_children(api_instance, drive=drive, parent=new_folder)
50-
# fetch files by relative path from root of drive
51-
path_children = files.DriveItem.get_children(api_instance, drive=drive, path='Microsoft Teams Chat Files')
52-
print(path_children)
53-
54-
# delete newly created folder
55-
new_folder.delete(api_instance, drive=drive)
4+
if __name__ == '__main__':
5+
import argparse
6+
authority_host_uri = 'https://login.microsoftonline.com'
7+
tenant = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
8+
resource_uri = 'https://graph.microsoft.com'
9+
client_id = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
10+
client_thumbprint = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
11+
12+
parser = argparse.ArgumentParser(description='Microsoft Graph Files Test')
13+
parser.add_argument('-t', '--tenant', dest='tenant', type=str, help='Microsoft Graph Tenant', default=os.getenv('DATACENTER'))
14+
parser.add_argument('-c', '--clientid', dest='client_id', type=str, help='Microsoft Graph Client ID', default=os.getenv('CLIENTID'))
15+
parser.add_argument('--thumbprint', dest='thumbprint', type=str, help='Microsoft Graph Secret', default=os.getenv('CLIENTSECRET'))
16+
parser.add_argument('--certificate', dest='certificate', type=str, help='Microsoft Graph Certificate', default=os.getenv('CERTIFICATE'))
17+
parser.add_argument('--user', dest='user', type=str, required=True, help='Microsoft Graph Test User')
18+
19+
args = parser.parse_args()
20+
21+
if os.path.exists(args.certificate):
22+
with open(args.certificate, 'rb') as input_file:
23+
client_certificate = input_file.read()
24+
else:
25+
client_certificate = args.certificate
26+
api_instance = api.GraphAPI.from_certificate(authority_host_uri, args.tenant, resource_uri, args.client_id, client_certificate, args.thumbprint)
27+
28+
test_user = user.User.get(api_instance, args.user)
29+
# fetch a OneDrive of a given user
30+
drive = files.Drive.get(api_instance, user=test_user)
31+
# fetch the OneDrive for a given site
32+
# drive = files.Drive.get(api_instance, site='a258178f-da15-42dd-a85b-90dbe49ebd9e')
33+
34+
# fetch drives accessible by a given user
35+
accessible_drives = files.Drive.accessible(api_instance, user=args.user)
36+
print(accessible_drives)
37+
38+
# fetch the root folder of the drive
39+
drive_root_folder = files.DriveItem.root_folder(api_instance, drive=drive)
40+
41+
# create a new folder in the root folder
42+
new_folder = files.DriveItem.create_folder(api_instance, "Test", drive_root_folder, drive=drive)
43+
# create a file in the new folder
44+
new_file = files.DriveItem.upload(api_instance, 'Hello, World!', drive=drive, parent=new_folder, file_name='This is a test.txt')
45+
46+
# overwrite the file in the new folder
47+
overwriting_file = files.DriveItem.upload(api_instance, 'Hello, John!', drive=drive, parent_id=new_folder, item=new_file, replace=True)
48+
49+
# update the file name of the file
50+
overwriting_file.name = 'My new file name.txt'
51+
overwriting_file.update(api_instance, drive=drive)
52+
# or alternatively
53+
overwriting_file.move(api_instance, 'My new file name.txt', folder=drive_root_folder, drive=drive)
54+
55+
# search for .txt files on the drive
56+
search_results = files.DriveItem.search(api_instance, 'txt', drive=drive)
57+
# delete the created file
58+
overwriting_file.delete(api_instance, drive=drive)
59+
60+
# fetch children of the root directory of a drive
61+
root_children = files.DriveItem.get_children(api_instance, drive=drive)
62+
# fetch children of parent folder
63+
folder_children = files.DriveItem.get_children(api_instance, drive=drive, parent=new_folder)
64+
# fetch files by relative path from root of drive
65+
path_children = files.DriveItem.get_children(api_instance, drive=drive, path='Microsoft Teams Chat Files')
66+
print(path_children)
67+
68+
# delete newly created folder
69+
new_folder.delete(api_instance, drive=drive)

msgraph/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.2.0'
1+
__version__ = '0.2.5'

msgraph/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def request(self, uri, **kwargs):
114114
version = kwargs.get('version', '1.0')
115115
method = kwargs.pop('method', 'GET')
116116
if self.resource_uri not in uri:
117-
url = os.path.join(self.resource_uri, 'v%s' % version, uri)
117+
url = '%s/%s/%s' % (self.resource_uri, 'v%s' % version, uri)
118118
else:
119119
url = uri
120120
token = str(self._access_token)

msgraph/base.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from datetime import datetime
2+
3+
4+
class Base(object):
5+
date_format = '%Y-%m-%d'
6+
time_format = '%H:%M:%S'
7+
datetime_format = date_format + 'T%s' % time_format
8+
full_datetime_format = date_format + 'T' + time_format + '%f'
9+
iso_format = date_format + 'T%sZ' % time_format
10+
standard_datetime_format = date_format + ' ' + time_format
11+
extended_datetime_format = date_format + 'T' + time_format +'.%fZ'
12+
13+
@classmethod
14+
def parse_date_time(cls, text):
15+
instance = None
16+
formats = [cls.extended_datetime_format, cls.full_datetime_format, cls.datetime_format, cls.standard_datetime_format, cls.iso_format, cls.date_format]
17+
for format in formats:
18+
try:
19+
instance = datetime.strptime(text, format)
20+
except Exception:
21+
pass
22+
else:
23+
break
24+
return instance

msgraph/calendar.py

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
11
import logging
2-
from datetime import datetime
32

43

54
logger = logging.getLogger(__name__)
65

7-
date_format = '%Y-%m-%d'
8-
datetime_format = date_format + 'T%H:%M:%S'
9-
full_datetime_format = date_format + 'T%H:%M:%S.%f'
10-
extended_datetime_format = date_format + 'T%H:%M:%S.%fZ'
116

12-
13-
def parse_date_times(text):
14-
instance = None
15-
for format in [extended_datetime_format, full_datetime_format, datetime_format, date_format]:
16-
try:
17-
instance = datetime.strptime(text, format)
18-
except Exception:
19-
pass
20-
else:
21-
break
22-
return instance
23-
24-
25-
class Calendar(object):
7+
class Calendar(base.Base)
268
__slots__ = ('id', 'name', 'owner', 'color', 'can_edit', 'can_share', 'can_view_private_items', 'change_key')
279

2810
def __init__(self, id, name, owner, color, can_edit, can_share, can_view_private_items, change_key):
@@ -136,7 +118,7 @@ def get(cls, api, **kwargs):
136118
return output
137119

138120

139-
class Location(object):
121+
class Location(base.Base)
140122
__slots__ = ('display_name', 'location_type', 'unique_id', 'unique_id_type')
141123

142124
def __init__(self, display_name, location_type, unique_id, unique_id_type):
@@ -160,7 +142,7 @@ def from_api(cls, data):
160142
return cls(display_name, location_type, unique_id, unique_id_type)
161143

162144

163-
class Attendee(object):
145+
class Attendee(base.Base)
164146
__slots__ = ('name', 'email_address', 'type', 'status', 'response_time')
165147

166148
def __init__(self, name, email_address, type, status, response_time):
@@ -188,11 +170,11 @@ def from_api(cls, data):
188170
status = status_data.get('response')
189171
response_time = status_data.get('time')
190172
if response_time:
191-
response_time = parse_date_times(response_time)
173+
response_time = cls.parse_date_time(response_time)
192174
return cls(name, email_address, type, status, response_time)
193175

194176

195-
class Category(object):
177+
class Category(base.Base)
196178
__slots__ = ('id', 'display_name', 'color')
197179

198180
def __init__(self, id, display_name, color):
@@ -313,7 +295,7 @@ def create(cls, api, display_name, color, **kwargs):
313295
return instance
314296

315297

316-
class Range(object):
298+
class Range(base.Base)
317299
__slots__ = ('type', 'start_date', 'end_date')
318300

319301
def __init__(self, type, start_date, end_date):
@@ -325,19 +307,19 @@ def __repr__(self):
325307
return '<%s %s type=%r start_date=%r, end_date=%r>' % (self.__class__.__name__, id(self), self.type, self.start_date, self.end_date)
326308

327309
def to_dict(self):
328-
start_date = self.start_date.strftime(date_format)
329-
end_date = self.end_date.strftime(date_format)
310+
start_date = self.start_date.strftime(self.date_format)
311+
end_date = self.end_date.strftime(self.date_format)
330312
return dict(type=type, startDate=start_date, endDate=end_date)
331313

332314
@classmethod
333315
def from_api(cls, data):
334316
type = data['type']
335-
start_date = parse_date_times(data['startDate'])
336-
end_date = parse_date_times(data['endDate'])
317+
start_date = cls.parse_date_time(data['startDate'])
318+
end_date = cls.parse_date_time(data['endDate'])
337319
return cls(type, start_date, end_date)
338320

339321

340-
class DateTime(object):
322+
class DateTime(base.Base)
341323
__slots__ = ('date_time', 'time_zone')
342324

343325
def __init__(self, date_time, time_zone):
@@ -348,19 +330,19 @@ def __repr__(self):
348330
return '<%s %s date_time=%r, time_zone=%r>' % (self.__class__.__name__, id(self), self.date_time, self.time_zone)
349331

350332
def to_dict(self):
351-
date_time = self.date_time.strftime(datetime_format)
333+
date_time = self.date_time.strftime(self.datetime_format)
352334
time_zone = self.time_zone
353335
return dict(dateTime=date_time, timeZone=time_zone)
354336

355337
@classmethod
356338
def from_api(cls, data):
357339
date_time = data['dateTime']
358-
date_time = parse_date_times(date_time[:26])
340+
date_time = cls.parse_date_time(date_time[:26])
359341
time_zone = data['timeZone']
360342
return cls(date_time, time_zone)
361343

362344

363-
class Event(object):
345+
class Event(base.Base)
364346
__slots__ = ('id', 'ical_uid', 'series_master_id', 'type', 'categories', 'subject', 'body', 'body_preview', 'attendees', 'locations', 'location', 'start', 'original_start', 'original_start_time_zone', 'end', 'original_end', 'original_end_time_zone', 'is_all_day', 'is_cancelled', 'is_reminder_on', 'is_organizer', 'organizer', 'importance', 'sensitivity', 'recurrence', 'response_requested', 'response_status', 'reminder_minutes_before_start', 'show_as', 'online_meeting_url', 'web_link', 'has_attachments', 'attachments', 'calendar', 'extensions', 'instances', 'multi_value_extended_properties', 'single_value_extended_properties', 'created_at', 'last_modified', 'removed')
365347

366348
def __init__(self, id, ical_uid, series_master_id, type, categories, subject, body, body_preview, attendees, locations, location, start, original_start, original_start_time_zone, end, original_end, original_end_time_zone, is_all_day, is_cancelled, is_reminder_on, is_organizer, organizer, importance, sensitivity, recurrence, response_requested, response_status, reminder_minutes_before_start, show_as, online_meeting_url, web_link, has_attachments, attachments, calendar, extensions, instances, multi_value_extended_properties, single_value_extended_properties, created_at, last_modified, removed):
@@ -522,12 +504,12 @@ def from_api(cls, data):
522504
start = DateTime.from_api(data['start'])
523505
original_start = data.get('originalStart')
524506
if original_start:
525-
original_start = parse_date_times(original_start[:26])
507+
original_start = cls.parse_date_time(original_start[:26])
526508
original_start_time_zone = data.get('originalStartTimeZone')
527509
end = DateTime.from_api(data['end'])
528510
original_end = data.get('originalEnd')
529511
if original_end:
530-
original_end = parse_date_times(original_end[:26])
512+
original_end = cls.parse_date_time(original_end[:26])
531513
original_end_time_zone = data.get('originalEndTimeZone')
532514
is_all_day = data['isAllDay']
533515
is_cancelled = data['isCancelled']
@@ -553,8 +535,8 @@ def from_api(cls, data):
553535
instances = data.get('instances', [])
554536
multi_value_extended_properties = data.get('multiValueEextendedProperties', [])
555537
single_value_extended_properties = data.get('singleValueExtendedProperties', [])
556-
created_at = parse_date_times(data['createdDateTime'][:26])
557-
last_modified = parse_date_times(data['lastModifiedDateTime'][:26])
538+
created_at = cls.parse_date_time(data['createdDateTime'][:26])
539+
last_modified = cls.parse_date_time(data['lastModifiedDateTime'][:26])
558540
removed = data.get('@removed')
559541
return cls(id, ical_uid, series_master_id, type, categories, subject, body, body_preview, attendees, locations, location, start, original_start, original_start_time_zone, end, original_end, original_end_time_zone, is_all_day, is_cancelled, is_reminder_on, is_organizer, organizer, importance, sensitivity, recurrence, response_requested, response_status, reminder_minutes_before_start, show_as, online_meeting_url, web_link, has_attachments, attachments, calendar, extensions, instances, multi_value_extended_properties, single_value_extended_properties, created_at, last_modified, removed)
560542

@@ -579,8 +561,8 @@ def delta(cls, api, start, end, **kwargs):
579561
"""
580562
fields = kwargs.get('fields', ['id', 'seriesMasterId', 'type', 'categories', 'subject', 'body', 'bodyPreview', 'attendees', 'locations', 'location', 'start', 'end', 'isAllDay', 'isCancelled', 'isReminderOn', 'isOrganizer', 'originalStart', 'originalStartTimeZone', 'originalEndTimeZone', 'organizer', 'importance', 'sensitivity', 'recurrence', 'responseRequested', 'responseStatus', 'reminderMinutesBeforeStart', 'showAs', 'onlineMeetingUrl', 'webLink', 'hasAttachments', 'attachments', 'calendar', 'extensions', 'instances', 'createdDateTime', 'lastModifiedDateTime'])
581563
user = kwargs.get('user')
582-
start_formatted = start.strftime(datetime_format)
583-
end_formatted = end.strftime(datetime_format)
564+
start_formatted = start.strftime(cls.datetime_format)
565+
end_formatted = end.strftime(cls.datetime_format)
584566
if user:
585567
uri = 'users/%s/calendarView/delta' % user
586568
else:
@@ -689,7 +671,7 @@ def create(cls, api, subject, body, **kwargs):
689671
sensitivity (str): Sensitivity of the Event. Possible values are: normal, personal, private, confidential
690672
recurrence (dict): The recurrence pattern for the Event
691673
response_requested (bool): Indicates if potential attendees need to respond
692-
response_status (object): Indicates the type of response sent in response to an event message.
674+
response_status (base.Base) Indicates the type of response sent in response to an event message.
693675
reminder_minutes_before_start (int): The number of minutes before the event start time that the reminder alert occurs.
694676
show_as (str): The status to show for the User during the Event
695677
"""
@@ -737,7 +719,7 @@ def create(cls, api, subject, body, **kwargs):
737719
return instance
738720

739721

740-
class Group(object):
722+
class Group(base.Base)
741723
__slots__ = ('id', 'name', 'class_id', 'change_key')
742724

743725
def __init__(self, id, name, class_id, change_key):
@@ -856,7 +838,7 @@ def create(cls, api, name, class_id, change_key, **kwargs):
856838
return instance
857839

858840

859-
class Attachment(object):
841+
class Attachment(base.Base)
860842
"""
861843
Attachment instance representing a file attachment to an Event
862844
@@ -886,14 +868,14 @@ def __repr__(self):
886868
return '<%s %s id=%r, name=%r, is_inline=%s, size=%r, last_modified_datetime=%r>' % (self.__class__.__name__, id(self), self.id, self.name, self.is_inline, self.size, self.last_modified_datetime)
887869

888870
@classmethod
889-
def from_api(self, data):
871+
def from_api(cls, data):
890872
id = data['id']
891873
name = data['name']
892874
is_inline = data['isInline']
893875
size = data['size']
894876
raw_last_modified_datetime = data['lastModifiedDateTime']
895877
if raw_last_modified_datetime:
896-
last_modified_datetime = datetime.strptime(raw_last_modified_datetime[:-1], datetime_format)
878+
last_modified_datetime = cls.parse_date_time(raw_last_modified_datetime[:-1])
897879
return cls(id, name, is_inline, size, content_id, content_type, content_location, content_bytes, last_modified_datetime)
898880

899881
@classmethod
@@ -1003,7 +985,7 @@ def from_api(cls, data):
1003985
content_location = data['contentLocation']
1004986
content_bytes = data['contentBytes']
1005987
if raw_last_modified_datetime:
1006-
last_modified_datetime = datetime.strptime(raw_last_modified_datetime[:-1], datetime_format)
988+
last_modified_datetime = cls.parse_date_time(raw_last_modified_datetime[:-1])
1007989
return cls(id, name, is_inline, size, content_id, content_type, content_location, content_bytes, last_modified_datetime)
1008990

1009991
@classmethod

0 commit comments

Comments
 (0)