Skip to content

Commit e0ce09e

Browse files
author
Alejandro Casanovas
committed
new prefer timezone set for protocols
Added prefer header when replying to messages fixed some imports and styling
1 parent 82407b9 commit e0ce09e

File tree

5 files changed

+63
-37
lines changed

5 files changed

+63
-37
lines changed

O365/account.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Type, Tuple, Optional, Callable
22
from .connection import Connection, Protocol, MSGraphProtocol, MSOffice365Protocol
3-
from .utils import ME_RESOURCE, consent
3+
from .utils import ME_RESOURCE, consent_input_token
44

55

66
class Account:
@@ -81,7 +81,7 @@ def is_authenticated(self) -> bool:
8181
return token is not None and not token.is_expired
8282

8383
def authenticate(self, *, scopes: Optional[list] = None,
84-
handle_consent: Callable = consent.consent_input_token, **kwargs) -> bool:
84+
handle_consent: Callable = consent_input_token, **kwargs) -> bool:
8585
""" Performs the oauth authentication flow using the console resulting in a stored token.
8686
It uses the credentials passed on instantiation.
8787
Returns True if succeded otherwise False.

O365/connection.py

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from stringcase import pascalcase, camelcase, snakecase
1717
from tzlocal import get_localzone
1818
from zoneinfo import ZoneInfoNotFoundError, ZoneInfo
19-
from .utils import ME_RESOURCE, BaseTokenBackend, FileSystemTokenBackend, Token
19+
from .utils import ME_RESOURCE, BaseTokenBackend, FileSystemTokenBackend, Token, get_windows_tz
2020
import datetime as dt
2121

2222
log = logging.getLogger(__name__)
@@ -100,20 +100,12 @@ def __init__(self, *, protocol_url: Optional[str] = None,
100100
self.use_default_casing: bool = True if casing_function is None else False
101101
self.casing_function: Callable = casing_function or camelcase
102102

103+
# get_localzone() from tzlocal will try to get the system local timezone and if not will return UTC
104+
self._timezone: ZoneInfo = get_localzone()
105+
103106
if timezone:
104-
if isinstance(timezone, str):
105-
# convert string to ZoneInfo
106-
try:
107-
timezone = ZoneInfo(timezone)
108-
except ZoneInfoNotFoundError as e:
109-
log.error(f'Timezone {timezone} could not be found.')
110-
raise e
111-
else:
112-
if not isinstance(timezone, ZoneInfo):
113-
raise ValueError(f'The timezone parameter must be either a string or a valid ZoneInfo instance.')
107+
self.timezone = timezone # property setter will convert this timezone to ZoneInfo if a string is provided
114108

115-
# get_localzone() from tzlocal will try to get the system local timezone and if not will return UTC
116-
self.timezone: ZoneInfo = timezone or get_localzone()
117109
self.max_top_value: int = 500 # Max $top parameter value
118110

119111
# define any keyword that can be different in this protocol
@@ -122,6 +114,28 @@ def __init__(self, *, protocol_url: Optional[str] = None,
122114
# outlook = #Microsoft.OutlookServices.FileAttachment')
123115
self.keyword_data_store: dict = {}
124116

117+
@property
118+
def timezone(self):
119+
return self._timezone
120+
121+
@timezone.setter
122+
def timezone(self, timezone: Union[str, ZoneInfo]):
123+
self._update_timezone(timezone)
124+
125+
def _update_timezone(self, timezone: Union[str, ZoneInfo]):
126+
"""Sets the timezone. This is not done in the setter as you can't call super from a overriden setter """
127+
if isinstance(timezone, str):
128+
# convert string to ZoneInfo
129+
try:
130+
timezone = ZoneInfo(timezone)
131+
except ZoneInfoNotFoundError as e:
132+
log.error(f'Timezone {timezone} could not be found.')
133+
raise e
134+
else:
135+
if not isinstance(timezone, ZoneInfo):
136+
raise ValueError(f'The timezone parameter must be either a string or a valid ZoneInfo instance.')
137+
self._timezone = timezone
138+
125139
def get_service_keyword(self, keyword: str) -> str:
126140
""" Returns the data set to the key in the internal data-key dict
127141
@@ -226,12 +240,16 @@ def __init__(self, api_version='v1.0', default_resource=None,
226240

227241
self.keyword_data_store['message_type'] = 'microsoft.graph.message'
228242
self.keyword_data_store['event_message_type'] = 'microsoft.graph.eventMessage'
229-
self.keyword_data_store[
230-
'file_attachment_type'] = '#microsoft.graph.fileAttachment'
231-
self.keyword_data_store[
232-
'item_attachment_type'] = '#microsoft.graph.itemAttachment'
243+
self.keyword_data_store['file_attachment_type'] = '#microsoft.graph.fileAttachment'
244+
self.keyword_data_store['item_attachment_type'] = '#microsoft.graph.itemAttachment'
245+
self.keyword_data_store['prefer_timezone_header'] = f'outlook.timezone="{get_windows_tz(self._timezone)}"'
233246
self.max_top_value = 999 # Max $top parameter value
234247

248+
@Protocol.timezone.setter
249+
def timezone(self, timezone: Union[str, ZoneInfo]):
250+
super()._update_timezone(timezone)
251+
self.keyword_data_store['prefer_timezone_header'] = f'outlook.timezone="{get_windows_tz(self._timezone)}"'
252+
235253

236254
class MSOffice365Protocol(Protocol):
237255
""" A Microsoft Office 365 Protocol Implementation
@@ -261,18 +279,18 @@ def __init__(self, api_version='v2.0', default_resource=None,
261279
protocol_scope_prefix=self._oauth_scope_prefix,
262280
**kwargs)
263281

264-
self.keyword_data_store[
265-
'message_type'] = 'Microsoft.OutlookServices.Message'
266-
self.keyword_data_store[
267-
'event_message_type'] = 'Microsoft.OutlookServices.EventMessage'
268-
self.keyword_data_store[
269-
'file_attachment_type'] = '#Microsoft.OutlookServices.' \
270-
'FileAttachment'
271-
self.keyword_data_store[
272-
'item_attachment_type'] = '#Microsoft.OutlookServices.' \
273-
'ItemAttachment'
282+
self.keyword_data_store['message_type'] = 'Microsoft.OutlookServices.Message'
283+
self.keyword_data_store['event_message_type'] = 'Microsoft.OutlookServices.EventMessage'
284+
self.keyword_data_store['file_attachment_type'] = '#Microsoft.OutlookServices.FileAttachment'
285+
self.keyword_data_store['item_attachment_type'] = '#Microsoft.OutlookServices.ItemAttachment'
286+
self.keyword_data_store['prefer_timezone_header'] = f'outlook.timezone="{get_windows_tz(self.timezone)}"'
274287
self.max_top_value = 999 # Max $top parameter value
275288

289+
@Protocol.timezone.setter
290+
def timezone(self, timezone: Union[str, ZoneInfo]):
291+
super()._update_timezone(timezone)
292+
self.keyword_data_store['prefer_timezone_header'] = f'outlook.timezone="{get_windows_tz(self._timezone)}"'
293+
276294

277295
class MSBusinessCentral365Protocol(Protocol):
278296
""" A Microsoft Business Central Protocol Implementation
@@ -314,12 +332,16 @@ def __init__(self, api_version='v1.0', default_resource=None, environment=None,
314332

315333
self.keyword_data_store['message_type'] = 'microsoft.graph.message'
316334
self.keyword_data_store['event_message_type'] = 'microsoft.graph.eventMessage'
317-
self.keyword_data_store[
318-
'file_attachment_type'] = '#microsoft.graph.fileAttachment'
319-
self.keyword_data_store[
320-
'item_attachment_type'] = '#microsoft.graph.itemAttachment'
335+
self.keyword_data_store['file_attachment_type'] = '#microsoft.graph.fileAttachment'
336+
self.keyword_data_store['item_attachment_type'] = '#microsoft.graph.itemAttachment'
337+
self.keyword_data_store['prefer_timezone_header'] = f'outlook.timezone="{get_windows_tz(self.timezone)}"'
321338
self.max_top_value = 999 # Max $top parameter value
322339

340+
@Protocol.timezone.setter
341+
def timezone(self, timezone: Union[str, ZoneInfo]):
342+
super()._update_timezone(timezone)
343+
self.keyword_data_store['prefer_timezone_header'] = f'outlook.timezone="{get_windows_tz(self._timezone)}"'
344+
323345

324346
class Connection:
325347
""" Handles all communication (requests) between the app and the server """

O365/message.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ def single_value_extended_properties(self):
623623
return self.__single_value_extended_properties
624624

625625
def to_api_data(self, restrict_keys=None):
626-
""" Returns a dict representation of this message prepared to be send
626+
""" Returns a dict representation of this message prepared to be sent
627627
to the cloud
628628
629629
:param restrict_keys: a set of keys to restrict the returned
@@ -749,7 +749,10 @@ def reply(self, to_all=True):
749749
url = self.build_url(
750750
self._endpoints.get('create_reply').format(id=self.object_id))
751751

752-
response = self.con.post(url)
752+
# set prefer timezone header to protocol timezone
753+
headers = {'Prefer': self.protocol.get_service_keyword('prefer_timezone_header')}
754+
response = self.con.post(url, headers=headers)
755+
753756
if not response:
754757
return None
755758

O365/utils/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
from .utils import NEXT_LINK_KEYWORD, ME_RESOURCE, USERS_RESOURCE
66
from .utils import OneDriveWellKnowFolderNames, Pagination, Query
77
from .token import BaseTokenBackend, Token, FileSystemTokenBackend, FirestoreBackend, AWSS3Backend, AWSSecretsBackend, EnvTokenBackend
8-
from .windows_tz import IANA_TO_WIN, WIN_TO_IANA
9-
from . import consent
8+
from .windows_tz import get_iana_tz, get_windows_tz
9+
from .consent import consent_input_token

O365/utils/attachment.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
UPLOAD_SIZE_LIMIT_SIMPLE = 1024 * 1024 * 3 # 3 MB
1111
DEFAULT_UPLOAD_CHUNK_SIZE = 1024 * 1024 * 3
1212

13+
1314
class AttachableMixin:
1415
def __init__(self, attachment_name_property=None, attachment_type=None):
1516
""" Defines the functionality for an object to be attachable.

0 commit comments

Comments
 (0)