wbintegrator_office365 1.43.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wbintegrator_office365/__init__.py +1 -0
- wbintegrator_office365/admin.py +209 -0
- wbintegrator_office365/apps.py +5 -0
- wbintegrator_office365/configurations/__init__.py +0 -0
- wbintegrator_office365/configurations/configurations/__init__.py +23 -0
- wbintegrator_office365/dynamic_preferences_registry.py +15 -0
- wbintegrator_office365/factories.py +102 -0
- wbintegrator_office365/filters.py +237 -0
- wbintegrator_office365/importer/__init__.py +3 -0
- wbintegrator_office365/importer/api.py +403 -0
- wbintegrator_office365/importer/disable_signals.py +43 -0
- wbintegrator_office365/importer/parser.py +135 -0
- wbintegrator_office365/kpi_handlers/__init__.py +1 -0
- wbintegrator_office365/kpi_handlers/calls.py +114 -0
- wbintegrator_office365/migrations/0001_initial_squashed_squashed_0003_alter_calendar_owner_alter_calendarevent_organizer_and_more.py +677 -0
- wbintegrator_office365/migrations/0002_remove_calendar_owner_remove_calendarevent_activity_and_more.py +85 -0
- wbintegrator_office365/migrations/0003_alter_event_options.py +20 -0
- wbintegrator_office365/migrations/__init__.py +0 -0
- wbintegrator_office365/models/__init__.py +3 -0
- wbintegrator_office365/models/event.py +623 -0
- wbintegrator_office365/models/subscription.py +144 -0
- wbintegrator_office365/models/tenant.py +62 -0
- wbintegrator_office365/serializers.py +266 -0
- wbintegrator_office365/tasks.py +108 -0
- wbintegrator_office365/templates/admin/tenant_change_list.html +12 -0
- wbintegrator_office365/tests/__init__.py +0 -0
- wbintegrator_office365/tests/conftest.py +28 -0
- wbintegrator_office365/tests/test_admin.py +86 -0
- wbintegrator_office365/tests/test_models.py +65 -0
- wbintegrator_office365/tests/test_tasks.py +318 -0
- wbintegrator_office365/tests/test_views.py +128 -0
- wbintegrator_office365/tests/tests.py +12 -0
- wbintegrator_office365/urls.py +46 -0
- wbintegrator_office365/viewsets/__init__.py +31 -0
- wbintegrator_office365/viewsets/display.py +306 -0
- wbintegrator_office365/viewsets/endpoints.py +52 -0
- wbintegrator_office365/viewsets/menu.py +65 -0
- wbintegrator_office365/viewsets/titles.py +49 -0
- wbintegrator_office365/viewsets/viewsets.py +745 -0
- wbintegrator_office365-1.43.1.dist-info/METADATA +10 -0
- wbintegrator_office365-1.43.1.dist-info/RECORD +42 -0
- wbintegrator_office365-1.43.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
import json
|
2
|
+
from datetime import timedelta
|
3
|
+
|
4
|
+
import pandas as pd
|
5
|
+
import requests
|
6
|
+
from django.conf import settings
|
7
|
+
from django.utils import timezone
|
8
|
+
from dynamic_preferences.registries import global_preferences_registry
|
9
|
+
from rest_framework import status
|
10
|
+
|
11
|
+
from .parser import parse
|
12
|
+
|
13
|
+
|
14
|
+
class MicrosoftGraphAPI:
|
15
|
+
def __init__(self):
|
16
|
+
self.authority = getattr(settings, "WBINTEGRATOR_OFFICE365_AUTHORITY", "")
|
17
|
+
self.client_id = getattr(settings, "WBINTEGRATOR_OFFICE365_CLIENT_ID", "")
|
18
|
+
self.client_secret = getattr(settings, "WBINTEGRATOR_OFFICE365_CLIENT_SECRET", "")
|
19
|
+
self.redirect_uri = getattr(settings, "WBINTEGRATOR_OFFICE365_REDIRECT_URI", "")
|
20
|
+
self.token_endpoint = getattr(settings, "WBINTEGRATOR_OFFICE365_TOKEN_ENDPOINT", "")
|
21
|
+
self.notification_url = getattr(settings, "WBINTEGRATOR_OFFICE365_NOTIFICATION_URL", "")
|
22
|
+
self.graph_url = getattr(settings, "WBINTEGRATOR_OFFICE365_GRAPH_URL", "")
|
23
|
+
|
24
|
+
global_preferences = global_preferences_registry.manager()
|
25
|
+
if global_preferences["wbintegrator_office365__access_token"] == "0":
|
26
|
+
global_preferences["wbintegrator_office365__access_token"] = self._get_access_token()
|
27
|
+
|
28
|
+
def _get_administrator_consent(self):
|
29
|
+
url = f"{self.authority}/adminconsent?client_id={self.client_id}&state=12345&redirect_uri={self.redirect_uri}"
|
30
|
+
response = self._query(url, access_token=False)
|
31
|
+
if response:
|
32
|
+
return response
|
33
|
+
else:
|
34
|
+
raise ValueError("get administrator consent does not return response 200")
|
35
|
+
|
36
|
+
def _get_access_token(self):
|
37
|
+
# Get administrator consent
|
38
|
+
self._get_administrator_consent()
|
39
|
+
# Get an access token
|
40
|
+
url = f"{self.authority}{self.token_endpoint}"
|
41
|
+
payload = {
|
42
|
+
"grant_type": "client_credentials",
|
43
|
+
"client_id": self.client_id,
|
44
|
+
"scope": "https://graph.microsoft.com/.default",
|
45
|
+
"client_secret": self.client_secret,
|
46
|
+
}
|
47
|
+
|
48
|
+
response = self._query(url, method="POST", data=payload, is_json=False, access_token=False)
|
49
|
+
data = None
|
50
|
+
if response:
|
51
|
+
if response.json():
|
52
|
+
data = response.json().get("access_token")
|
53
|
+
else:
|
54
|
+
raise ValueError(response, response.json())
|
55
|
+
return data
|
56
|
+
|
57
|
+
def _subscribe(self, resource, minutes, change_type):
|
58
|
+
data = {
|
59
|
+
"changeType": change_type,
|
60
|
+
"notificationUrl": self.notification_url,
|
61
|
+
"resource": resource,
|
62
|
+
"clientState": "secretClientValue",
|
63
|
+
"latestSupportedTlsVersion": "v1_2",
|
64
|
+
}
|
65
|
+
if minutes:
|
66
|
+
# maximum time of subscription 4230 minutes (under 3 days)
|
67
|
+
date = timezone.now() + timedelta(minutes=minutes)
|
68
|
+
date = date.strftime("%Y-%m-%dT%H:%M:%SZ")
|
69
|
+
data["expirationDateTime"] = date
|
70
|
+
|
71
|
+
url = f"{self.graph_url}/subscriptions"
|
72
|
+
response = self._query(url, method="POST", data=json.dumps(data))
|
73
|
+
data = None
|
74
|
+
if response:
|
75
|
+
if response.json():
|
76
|
+
data = parse(response.json(), scalar_value=True)
|
77
|
+
if data:
|
78
|
+
data = data[0]
|
79
|
+
else:
|
80
|
+
raise ValueError(response, response.json())
|
81
|
+
return data
|
82
|
+
|
83
|
+
def _unsubscribe(self, subscription_id):
|
84
|
+
url = f"{self.graph_url}/subscriptions/{subscription_id}"
|
85
|
+
return self._query(url, method="DELETE")
|
86
|
+
|
87
|
+
def _renew_subscription(self, subscription_id, minutes=4230):
|
88
|
+
url = f"{self.graph_url}/subscriptions/{subscription_id}"
|
89
|
+
date = timezone.now() + timedelta(minutes=minutes)
|
90
|
+
date = date.strftime("%Y-%m-%dT%H:%M:%SZ")
|
91
|
+
data = {"expirationDateTime": date}
|
92
|
+
response = self._query(url, method="PATCH", data=json.dumps(data))
|
93
|
+
data = None
|
94
|
+
if response:
|
95
|
+
if response.json():
|
96
|
+
data = parse(response.json(), scalar_value=True)
|
97
|
+
if data:
|
98
|
+
data = data[0]
|
99
|
+
else:
|
100
|
+
raise ValueError(response, response.json())
|
101
|
+
return data
|
102
|
+
|
103
|
+
def subscriptions(self):
|
104
|
+
url = f"{self.graph_url}/subscriptions"
|
105
|
+
# curl -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IlVUSmxpNTVOcDV1MHVDb3dwTWxpMThIdDdQRDZwV1VkMWNyNlRRYUNlNG8iLCJhbGciOiJSUzI1NiIsIng1dCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83ZWJhNTBhMi00N2QyLTQxODUtYWIwNy0yYTNjYTE4YzliNzYvIiwiaWF0IjoxNjE4MjE5ODIwLCJuYmYiOjE2MTgyMTk4MjAsImV4cCI6MTYxODIyMzcyMCwiYWlvIjoiRTJaZ1lBaVNGN3ZUS0tGbng4YTk2ak5mNjhFVEFBPT0iLCJhcHBfZGlzcGxheW5hbWUiOiJOb3RpZmljYXRpb25DYWxsVXNlckFwcGxpY2F0aW9uIiwiYXBwaWQiOiJkYjg5ZDYxNi0xYWJlLTQzNTgtYWRmNC0zYzE2ZWY3ZjQ0ZjAiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83ZWJhNTBhMi00N2QyLTQxODUtYWIwNy0yYTNjYTE4YzliNzYvIiwiaWR0eXAiOiJhcHAiLCJvaWQiOiIyNzNjODI1Yy00YTYyLTQyOWUtODdiZi0zOWYzZDY2ZTMwYWYiLCJyaCI6IjAuQVRvQW9sQzZmdEpIaFVHckJ5bzhvWXliZGhiV2lkdS1HbGhEcmZROEZ1OV9SUEE2QUFBLiIsInJvbGVzIjpbIkFjY2Vzc1Jldmlldy5SZWFkV3JpdGUuTWVtYmVyc2hpcCIsIk1haWwuUmVhZFdyaXRlIiwiVXNlci5SZWFkV3JpdGUuQWxsIiwiRGVsZWdhdGVkUGVybWlzc2lvbkdyYW50LlJlYWRXcml0ZS5BbGwiLCJDYWxlbmRhcnMuUmVhZCIsIk1haWwuUmVhZEJhc2ljLkFsbCIsIkdyb3VwLlJlYWQuQWxsIiwiQWNjZXNzUmV2aWV3LlJlYWRXcml0ZS5BbGwiLCJEaXJlY3RvcnkuUmVhZFdyaXRlLkFsbCIsIkNhbGxSZWNvcmRzLlJlYWQuQWxsIiwiSWRlbnRpdHlVc2VyRmxvdy5SZWFkLkFsbCIsIlVzZXIuSW52aXRlLkFsbCIsIkRpcmVjdG9yeS5SZWFkLkFsbCIsIlVzZXIuUmVhZC5BbGwiLCJVc2VyTm90aWZpY2F0aW9uLlJlYWRXcml0ZS5DcmVhdGVkQnlBcHAiLCJGaWxlcy5SZWFkLkFsbCIsIk1haWwuUmVhZCIsIkNoYXQuUmVhZC5BbGwiLCJVc2VyLkV4cG9ydC5BbGwiLCJJZGVudGl0eVByb3ZpZGVyLlJlYWQuQWxsIiwiQ2FsZW5kYXJzLlJlYWRXcml0ZSIsIklkZW50aXR5Umlza3lVc2VyLlJlYWQuQWxsIiwiQWNjZXNzUmV2aWV3LlJlYWQuQWxsIiwiTWFpbC5TZW5kIiwiVXNlci5NYW5hZ2VJZGVudGl0aWVzLkFsbCIsIkNvbnRhY3RzLlJlYWQiLCJJZGVudGl0eVJpc2tFdmVudC5SZWFkLkFsbCIsIk1haWwuUmVhZEJhc2ljIiwiQ2hhdC5SZWFkQmFzaWMuQWxsIiwiQ2FsbHMuQWNjZXNzTWVkaWEuQWxsIiwiQXBwbGljYXRpb24uUmVhZC5BbGwiLCJSZXBvcnRzLlJlYWQuQWxsIl0sInN1YiI6IjI3M2M4MjVjLTRhNjItNDI5ZS04N2JmLTM5ZjNkNjZlMzBhZiIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJFVSIsInRpZCI6IjdlYmE1MGEyLTQ3ZDItNDE4NS1hYjA3LTJhM2NhMThjOWI3NiIsInV0aSI6Im9ONGJ1anMzaGtlOUVoUTRCTlV6QUEiLCJ2ZXIiOiIxLjAiLCJ4bXNfdGNkdCI6MTU1ODY4MjUxMX0.Jw5jVFFK2HIHXkWpR61eVGdVv-NnwsMBpHDRUur6UXrUlxtPwLAVvaklEILzALUNKAWe2Ic0k737tAq1C10DCkHF3iJ5bXQWTD0yAux1pZrHNYMMJ-bRvAdcuaLys5Amtas8gcYhj8mSpHdiSclW_17TxPDacSjvDwYEFOjgzAUYUtsoR5Q_q2H14QVR-PFd9WreqcOVy4ELNBBUhFdFjmV8Cb0lLUujW5Q0zBniOrwyRN4VYSdGEuyogoCOeICoNBdgwnLjXq6BMv6CLLSpzwEKDp-ikK7SETDd1uyDADYOUg-YuJ_D-ZIiobUNpA4UnBMNpfrUrCDzF84iox0vWA" 'https://graph.microsoft.com/v1.0/subscriptions'
|
106
|
+
response = self._query(url)
|
107
|
+
data = None
|
108
|
+
if response:
|
109
|
+
if datum := response.json():
|
110
|
+
data = parse(datum.get("value"))
|
111
|
+
url = datum.get("@odata.nextLink")
|
112
|
+
while url:
|
113
|
+
response = self._query(url)
|
114
|
+
datum = response.json()
|
115
|
+
data += parse(datum.get("value"))
|
116
|
+
url = datum.get("@odata.nextLink")
|
117
|
+
|
118
|
+
else:
|
119
|
+
raise ValueError(response, response.json())
|
120
|
+
return data
|
121
|
+
|
122
|
+
def user(self, email):
|
123
|
+
query_params = {"$select": "id, userPrincipalName, displayName"}
|
124
|
+
url = f"{self.graph_url}/users/{email}"
|
125
|
+
response = self._query(url, params=query_params)
|
126
|
+
data = None
|
127
|
+
if response:
|
128
|
+
if response.json():
|
129
|
+
data = parse(response.json(), scalar_value=True)
|
130
|
+
if data:
|
131
|
+
data = data[0]
|
132
|
+
else:
|
133
|
+
raise ValueError(response, response.json())
|
134
|
+
return data
|
135
|
+
|
136
|
+
def users(self, filter_params=True):
|
137
|
+
query_params = {
|
138
|
+
"$select": "id,displayName,businessPhones,mobilePhone, userPrincipalName, mail,email, mailNickname, givenName, surname, imAddresses"
|
139
|
+
}
|
140
|
+
url = f"{self.graph_url}/users"
|
141
|
+
if filter_params:
|
142
|
+
response = self._query(url, params=query_params)
|
143
|
+
else:
|
144
|
+
response = self._query(url)
|
145
|
+
data = None
|
146
|
+
if response:
|
147
|
+
data = parse(response.json().get("value"))
|
148
|
+
else:
|
149
|
+
raise ValueError(response, response.json())
|
150
|
+
return data
|
151
|
+
|
152
|
+
def call(self, call_id: str, raise_error: bool = False):
|
153
|
+
url = f"{self.graph_url}/communications/callRecords/{call_id}"
|
154
|
+
response = self._query(url)
|
155
|
+
data = None
|
156
|
+
if response and (json_data := response.json()):
|
157
|
+
data = parse(pd.json_normalize(json_data))
|
158
|
+
if data:
|
159
|
+
data = data[0]
|
160
|
+
elif raise_error:
|
161
|
+
raise ValueError(response, response.json())
|
162
|
+
return data
|
163
|
+
|
164
|
+
# CREATION CALENDAR
|
165
|
+
def create_calendar_group(self, user_id, data):
|
166
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups"
|
167
|
+
return self._query_create(url, data)
|
168
|
+
|
169
|
+
def create_calendar(self, user_id, data, id_calendar_group=None):
|
170
|
+
if id_calendar_group:
|
171
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars"
|
172
|
+
else:
|
173
|
+
url = f"{self.graph_url}/users/{user_id}/calendars"
|
174
|
+
return self._query_create(url, data)
|
175
|
+
|
176
|
+
def create_calendar_event(self, user_id, data, id_calendar_group=None, id_calendar=None):
|
177
|
+
if id_calendar:
|
178
|
+
# A user's calendar in the default calendarGroup.
|
179
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}/events"
|
180
|
+
if id_calendar_group:
|
181
|
+
# A user's calendar in a specific calendarGroup.
|
182
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars/{id_calendar}/events"
|
183
|
+
else:
|
184
|
+
# A user's or group's default calendar.
|
185
|
+
url = f"{self.graph_url}/users/{user_id}/events"
|
186
|
+
return self._query_create(url, data)
|
187
|
+
|
188
|
+
# LIST CALENDAR
|
189
|
+
def get_list_calendar_groups(self, user_id):
|
190
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups"
|
191
|
+
return self._query_get_list(url)
|
192
|
+
|
193
|
+
def get_list_calendars(self, user_id, id_calendar_group=None):
|
194
|
+
if id_calendar_group:
|
195
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars"
|
196
|
+
else:
|
197
|
+
url = f"{self.graph_url}/users/{user_id}/calendars"
|
198
|
+
# Configure query parameters to modify the results
|
199
|
+
# query_params = {
|
200
|
+
# "$select": "subject, organizer, createdDateTime, lastModifiedDateTime, start, end, reminderMinutesBeforeStart, isReminderOn, recurrence, hasAttachments, importance, location, webLink, attendees",
|
201
|
+
# "$orderby": "createdDateTime DESC",
|
202
|
+
# }
|
203
|
+
return self._query_get_list(url)
|
204
|
+
|
205
|
+
def get_list_calendar_events(self, user_id, id_calendar=None, id_calendar_group=None):
|
206
|
+
if id_calendar:
|
207
|
+
# A user's calendar in the default calendarGroup.
|
208
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}/events"
|
209
|
+
if id_calendar_group:
|
210
|
+
# A user's calendar in a specific calendarGroup.
|
211
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars/{id_calendar}/events"
|
212
|
+
else:
|
213
|
+
# A user's or group's default calendar.
|
214
|
+
url = f"{self.graph_url}/users/{user_id}/events"
|
215
|
+
return self._query_get_list(url)
|
216
|
+
|
217
|
+
# GET CALENDAR
|
218
|
+
def get_calendar_group(self, user_id, id_calendar_group=None):
|
219
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}"
|
220
|
+
return self._query_get(url)
|
221
|
+
|
222
|
+
def get_calendar(self, user_id, id_calendar, id_calendar_group=None):
|
223
|
+
if id_calendar_group:
|
224
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars/{id_calendar}"
|
225
|
+
else:
|
226
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}"
|
227
|
+
return self._query_get(url)
|
228
|
+
|
229
|
+
def get_calendar_event(self, user_id, id_event, id_calendar=None, id_calendar_group=None):
|
230
|
+
if id_calendar:
|
231
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}/events/{id_event}"
|
232
|
+
if id_calendar_group:
|
233
|
+
url = f"{self.graph_url}/users/{user_id}/calendargroups/{id_calendar_group}/calendars/{id_calendar}/events/{id_event}"
|
234
|
+
else:
|
235
|
+
url = f"{self.graph_url}/users/{user_id}/events/{id_event}"
|
236
|
+
|
237
|
+
return self._query_get(url)
|
238
|
+
|
239
|
+
# FORWARD CALENDAR EVENT
|
240
|
+
def forward_calendar_event(self, user_id, id_event, data, id_calendar_group=None, id_calendar=None):
|
241
|
+
if id_calendar:
|
242
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}/events/{id_event}/forward"
|
243
|
+
if id_calendar_group:
|
244
|
+
url = f"{self.graph_url}/users/{user_id}/calendargroups/{id_calendar_group}/calendars/{id_calendar}/events/{id_event}/forward"
|
245
|
+
else:
|
246
|
+
url = f"{self.graph_url}/users/{user_id}/events/{id_event}/forward"
|
247
|
+
return self._query_create(url, data)
|
248
|
+
|
249
|
+
def get_calendar_event_by_resource(self, resource):
|
250
|
+
url = f"{self.graph_url}/{resource}"
|
251
|
+
return self._query_get(url)
|
252
|
+
|
253
|
+
def get_calendar_from_navigation_link(self, url):
|
254
|
+
return self._query_get(url)
|
255
|
+
|
256
|
+
# UPDATE CALENDAR
|
257
|
+
def update_calendar_group(self, user_id, id_calendar_group, data):
|
258
|
+
url = f"{self.graph_url}/users/{user_id}/calendargroups/{id_calendar_group}"
|
259
|
+
return self._query_update(url, data)
|
260
|
+
|
261
|
+
def update_calendar(self, user_id, id_calendar, data, id_calendar_group=None):
|
262
|
+
if id_calendar_group:
|
263
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars/{id_calendar}"
|
264
|
+
else:
|
265
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}"
|
266
|
+
return self._query_update(url, data)
|
267
|
+
|
268
|
+
def update_calendar_event(self, user_id, id_event, data, id_calendar=None, id_calendar_group=None):
|
269
|
+
if id_calendar:
|
270
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}/events/{id_event}"
|
271
|
+
if id_calendar_group:
|
272
|
+
url = f"{self.graph_url}/users/{user_id}/calendargroups/{id_calendar_group}/calendars/{id_calendar}/events/{id_event}"
|
273
|
+
else:
|
274
|
+
url = f"{self.graph_url}/users/{user_id}/events/{id_event}"
|
275
|
+
return self._query_update(url, data)
|
276
|
+
|
277
|
+
# DELETE CALENDAR
|
278
|
+
def delete_calendar_group(self, user_id, id_calendar_group):
|
279
|
+
url = f"{self.graph_url}/users/{user_id}/calendargroups/{id_calendar_group}"
|
280
|
+
return self._query_delete(url)
|
281
|
+
|
282
|
+
def delete_calendar(self, user_id, id_calendar, id_calendar_group=None):
|
283
|
+
if id_calendar_group:
|
284
|
+
url = f"{self.graph_url}/users/{user_id}/calendarGroups/{id_calendar_group}/calendars/{id_calendar}"
|
285
|
+
else:
|
286
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}"
|
287
|
+
return self._query_delete(url)
|
288
|
+
|
289
|
+
def delete_calendar_event(self, user_id, id_event, id_calendar=None, id_calendar_group=None):
|
290
|
+
if id_calendar:
|
291
|
+
url = f"{self.graph_url}/users/{user_id}/calendars/{id_calendar}/events/{id_event}"
|
292
|
+
if id_calendar_group:
|
293
|
+
url = f"{self.graph_url}/users/{user_id}/calendargroups/{id_calendar_group}/calendars/{id_calendar}/events/{id_event}"
|
294
|
+
else:
|
295
|
+
url = f"{self.graph_url}/users/{user_id}/events/{id_event}"
|
296
|
+
return self._query_delete(url)
|
297
|
+
|
298
|
+
def delete_calendar_event_by_resource(self, resource):
|
299
|
+
url = f"{self.graph_url}/{resource}"
|
300
|
+
return self._query_delete(url)
|
301
|
+
|
302
|
+
def get_or_create_workbench_calendar(self, user_id, calendar_name):
|
303
|
+
workbench_calendar = None
|
304
|
+
datum = self.get_list_calendars(user_id=user_id)
|
305
|
+
if calendar_name:
|
306
|
+
calendars = {calendar.get("name"): calendar.get("id") for calendar in datum}
|
307
|
+
if calendar_name in calendars.keys():
|
308
|
+
workbench_calendar = self.get_calendar(user_id, calendars[calendar_name])
|
309
|
+
else:
|
310
|
+
data = {"name": calendar_name}
|
311
|
+
workbench_calendar = self.create_calendar(user_id, data)
|
312
|
+
else:
|
313
|
+
for calendar in datum:
|
314
|
+
if calendar.get("is_default_calendar") is True:
|
315
|
+
workbench_calendar = self.get_calendar(user_id, calendar.get("id"))
|
316
|
+
return workbench_calendar
|
317
|
+
|
318
|
+
def _query_get_list(self, url):
|
319
|
+
response = self._query(url)
|
320
|
+
datum = None
|
321
|
+
if response:
|
322
|
+
if response.json():
|
323
|
+
if response.json().get("value"):
|
324
|
+
datum = parse(pd.json_normalize(response.json().get("value")))
|
325
|
+
else:
|
326
|
+
raise ValueError(response, response.json())
|
327
|
+
return datum
|
328
|
+
|
329
|
+
def _query_get(self, url, raise_error=False):
|
330
|
+
response = self._query(url)
|
331
|
+
data = None
|
332
|
+
if response:
|
333
|
+
if response.json():
|
334
|
+
data = parse(pd.json_normalize(response.json()))
|
335
|
+
if data:
|
336
|
+
data = data[0]
|
337
|
+
elif raise_error:
|
338
|
+
raise ValueError(response, response.json())
|
339
|
+
return data
|
340
|
+
|
341
|
+
def _query_create(self, url, data):
|
342
|
+
response = self._query(url, method="POST", data=json.dumps(data))
|
343
|
+
data = None
|
344
|
+
if response.status_code < 400:
|
345
|
+
try:
|
346
|
+
if response.json():
|
347
|
+
data = parse(pd.json_normalize(response.json()))
|
348
|
+
if data:
|
349
|
+
data = data[0]
|
350
|
+
except requests.exceptions.InvalidJSONError:
|
351
|
+
pass # print(response, response.__dict__)
|
352
|
+
|
353
|
+
else:
|
354
|
+
raise ValueError(response, response.__dict__)
|
355
|
+
return data
|
356
|
+
|
357
|
+
def _query_update(self, url, data):
|
358
|
+
response = self._query(url, method="PATCH", data=json.dumps(data))
|
359
|
+
data = None
|
360
|
+
if response:
|
361
|
+
if response.json():
|
362
|
+
data = parse(pd.json_normalize(response.json()))
|
363
|
+
else:
|
364
|
+
if response.status_code not in [status.HTTP_503_SERVICE_UNAVAILABLE, status.HTTP_409_CONFLICT]:
|
365
|
+
raise ValueError(response, response.json())
|
366
|
+
else:
|
367
|
+
import time
|
368
|
+
|
369
|
+
time.sleep(60)
|
370
|
+
if response := self._query(url, method="PATCH", data=json.dumps(data)):
|
371
|
+
if response.json():
|
372
|
+
data = parse(pd.json_normalize(response.json()))
|
373
|
+
return data
|
374
|
+
|
375
|
+
def _query_delete(self, url):
|
376
|
+
response = self._query(url, method="DELETE")
|
377
|
+
if response:
|
378
|
+
return response
|
379
|
+
else:
|
380
|
+
raise ValueError(response)
|
381
|
+
|
382
|
+
def _query(self, url, method="GET", data=None, params=None, access_token=True, is_json=True):
|
383
|
+
headers = {"content-type": "application/json" if is_json else "application/x-www-form-urlencoded"}
|
384
|
+
if access_token:
|
385
|
+
global_preferences = global_preferences_registry.manager()
|
386
|
+
headers["Authorization"] = f'Bearer {global_preferences["wbintegrator_office365__access_token"]}'
|
387
|
+
if method == "POST":
|
388
|
+
response = requests.post(url, data=data, headers=headers)
|
389
|
+
elif method == "DELETE":
|
390
|
+
response = requests.delete(url, headers=headers)
|
391
|
+
elif method == "PATCH":
|
392
|
+
response = requests.patch(url, data=data, headers=headers)
|
393
|
+
else:
|
394
|
+
response = requests.get(url, headers=headers, params=params)
|
395
|
+
if response.status_code == status.HTTP_401_UNAUTHORIZED:
|
396
|
+
new_token = self._get_access_token()
|
397
|
+
if new_token != global_preferences["wbintegrator_office365__access_token"]:
|
398
|
+
global_preferences["wbintegrator_office365__access_token"] = new_token
|
399
|
+
return self._query(
|
400
|
+
url, method=method, data=data, params=params, access_token=access_token, is_json=is_json
|
401
|
+
)
|
402
|
+
else:
|
403
|
+
return response
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from collections import defaultdict
|
2
|
+
|
3
|
+
from django.db.models.signals import (
|
4
|
+
post_delete,
|
5
|
+
post_init,
|
6
|
+
post_migrate,
|
7
|
+
post_save,
|
8
|
+
pre_delete,
|
9
|
+
pre_init,
|
10
|
+
pre_migrate,
|
11
|
+
pre_save,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
class DisableSignals(object):
|
16
|
+
def __init__(self, disabled_signals=None):
|
17
|
+
self.stashed_signals = defaultdict(list)
|
18
|
+
self.disabled_signals = disabled_signals or [
|
19
|
+
pre_init,
|
20
|
+
post_init,
|
21
|
+
pre_save,
|
22
|
+
post_save,
|
23
|
+
pre_delete,
|
24
|
+
post_delete,
|
25
|
+
pre_migrate,
|
26
|
+
post_migrate,
|
27
|
+
]
|
28
|
+
|
29
|
+
def __enter__(self):
|
30
|
+
for signal in self.disabled_signals:
|
31
|
+
self.disconnect(signal)
|
32
|
+
|
33
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
34
|
+
for signal in list(self.stashed_signals):
|
35
|
+
self.reconnect(signal)
|
36
|
+
|
37
|
+
def disconnect(self, signal):
|
38
|
+
self.stashed_signals[signal] = signal.receivers
|
39
|
+
signal.receivers = []
|
40
|
+
|
41
|
+
def reconnect(self, signal):
|
42
|
+
signal.receivers = self.stashed_signals.get(signal, [])
|
43
|
+
del self.stashed_signals[signal]
|
@@ -0,0 +1,135 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
|
3
|
+
|
4
|
+
def parse(_dict, scalar_value=False):
|
5
|
+
data = None
|
6
|
+
if scalar_value:
|
7
|
+
df = pd.DataFrame(_dict, index=[0])
|
8
|
+
else:
|
9
|
+
df = pd.DataFrame(_dict)
|
10
|
+
df = df.rename(columns=_map)
|
11
|
+
data = df.to_dict("records")
|
12
|
+
return data
|
13
|
+
|
14
|
+
|
15
|
+
_map = {
|
16
|
+
"lastModifiedDateTime": "last_modified",
|
17
|
+
"startDateTime": "start",
|
18
|
+
"endDateTime": "end",
|
19
|
+
"organizer": "organizer",
|
20
|
+
"displayName": "display_name",
|
21
|
+
"businessPhones": "business_phones",
|
22
|
+
"mobilePhone": "mobile_phone",
|
23
|
+
"surname": "surname",
|
24
|
+
"givenName": "given_name",
|
25
|
+
"mailNickname": "mail_nickname",
|
26
|
+
"userPrincipalName": "user_principal_name",
|
27
|
+
"notificationUrl": "notification_url",
|
28
|
+
"changeType": "change_type",
|
29
|
+
"expirationDateTime": "expiration",
|
30
|
+
"resourceData": "resource_data",
|
31
|
+
"subscriptionExpirationDateTime": "subscription_expiration",
|
32
|
+
"clientState": "client_state",
|
33
|
+
"tenantId": "tenant_id",
|
34
|
+
"subscriptionId": "subscription_id",
|
35
|
+
"organizer.user": "organizer.user",
|
36
|
+
"organizer.user.id": "organizer.user.id",
|
37
|
+
"organizer.user.displayName": "organizer.user.display_name",
|
38
|
+
"organizer.phone": "organizer.phone",
|
39
|
+
"organizer.phone.id": "organizer.phone.id",
|
40
|
+
"organizer.phone.displayName": "organizer.phone.display_name",
|
41
|
+
"organizer.guest": "organizer.guest",
|
42
|
+
"organizer.guest.id": "organizer.guest.id",
|
43
|
+
"organizer.guest.displayName": "organizer.guest.display_name",
|
44
|
+
"organizer.spoolUser": "organizer.splool_user",
|
45
|
+
"organizer.acsUser": "organizer.acs_user",
|
46
|
+
"organizer.encrypted": "organizer.encrypted",
|
47
|
+
"organizer.onPremises": "organizer.on_premises",
|
48
|
+
"organizer.acsApplicationInstance": "organizer.acs_appllication_instance",
|
49
|
+
"organizer.spoolApplicationInstance": "organizer.spool_application_instance",
|
50
|
+
"organizer.applicationInstance": "organizer.application_instance",
|
51
|
+
"organizer.application": "organizer.application",
|
52
|
+
"organizer.device": "organizer.device",
|
53
|
+
"joinWebUrl": "join_web_url",
|
54
|
+
"preferredLanguage": "preferred_language",
|
55
|
+
"officeLocation": "office_location",
|
56
|
+
"jobTitle": "job_title",
|
57
|
+
"createdDateTime": "created",
|
58
|
+
"isReminderOn": "is_reminder_on",
|
59
|
+
"reminderMinutesBeforeStart": "reminder_minutes_before_start",
|
60
|
+
"hasAttachments": "has_attachments",
|
61
|
+
"webLink": "web_link",
|
62
|
+
"start.dateTime": "start",
|
63
|
+
"start.timeZone": "start.time_zone",
|
64
|
+
"end.dateTime": "end",
|
65
|
+
"end.timeZone": "end.time_zone",
|
66
|
+
"location.displayName": "location.display_name",
|
67
|
+
"location.locationUri": "location.location_uri",
|
68
|
+
"location.locationType": "location.location_type",
|
69
|
+
"location.uniqueId": "location.unique_id",
|
70
|
+
"location.uniqueIdType": "location.unique_id_type",
|
71
|
+
"location.address.type": "location.address.type",
|
72
|
+
"location.address.street": "location.address.street",
|
73
|
+
"location.address.city": "location.address.city",
|
74
|
+
"location.address.postalCode": "location.address.postal_code",
|
75
|
+
"location.address.countryOrRegion": "location.address.country_or_region",
|
76
|
+
"recurrence.pattern.daysOfWeek": "recurrence.pattern.days_of_week",
|
77
|
+
"recurrence.pattern.dayOfMonth": "recurrence.pattern.day_of_month",
|
78
|
+
"recurrence.pattern.firstDayOfWeek": "recurrence.pattern.first_day_of_week",
|
79
|
+
"recurrence.range.recurrenceTimeZone": "recurrence.range.recurrence_time_zone",
|
80
|
+
"recurrence.range.numberOfOccurrences": "recurrence.range.number_of_occurrences",
|
81
|
+
"recurrence.range.startDate": "recurrence.range.start_date",
|
82
|
+
"recurrence.range.endDate": "recurrence.range.end_date",
|
83
|
+
"organizer.emailAddress.name": "organizer.email_address.name",
|
84
|
+
"organizer.emailAddress.address": "organizer.email_address.address",
|
85
|
+
"responseStatus.response": "response_status.response",
|
86
|
+
"responseStatus.time": "response_status.time",
|
87
|
+
"attendees": "participants",
|
88
|
+
"@odata.context": "odata_context",
|
89
|
+
"@odata.etag": "odata_etag",
|
90
|
+
"applicationId": "application_id",
|
91
|
+
"includeResourceData": "include_resource_data",
|
92
|
+
"latestSupportedTlsVersion": "latest_supported_tls_version",
|
93
|
+
"encryptionCertificate": "encryption_certificate",
|
94
|
+
"encryptionCertificateId": "encryption_certificate_id",
|
95
|
+
"notificationQueryOptions": "notification_query_options",
|
96
|
+
"notificationContentType": "notification_content_type",
|
97
|
+
"lifecycleNotificationUrl": "lifecycle_notification_url",
|
98
|
+
"creatorId": "creator_id",
|
99
|
+
"isDefaultCalendar": "is_default_calendar",
|
100
|
+
"hexColor": "hex_color",
|
101
|
+
"changeKey": "change_key",
|
102
|
+
"canShare": "can_share",
|
103
|
+
"canViewPrivateItems": "can_view_private_items",
|
104
|
+
"isShared": "is_shared",
|
105
|
+
"isSharedWithMe": "is_shared_with_me",
|
106
|
+
"canEdit": "can_edit",
|
107
|
+
"allowedOnlineMeetingProviders": "allowed_online_meeting_providers",
|
108
|
+
"defaultOnlineMeetingProvider": "default_online_meeting_provider",
|
109
|
+
"isTallyingResponses": "is_tallying_responses",
|
110
|
+
"isRemovable": "is_removable",
|
111
|
+
"teamsForBusiness": "teams_for_business",
|
112
|
+
"transactionId": "transaction_id",
|
113
|
+
"originalStartTimeZone": "original_start_time_zone",
|
114
|
+
"originalEndTimeZone": "original_end_time_zone",
|
115
|
+
"bodyPreview": "body_preview",
|
116
|
+
"isAllDay": "is_all_day",
|
117
|
+
"isCancelled": "is_cancelled",
|
118
|
+
"isOrganizer": "is_organizer",
|
119
|
+
"responseRequested": "response_requested",
|
120
|
+
"seriesMasterId": "series_master_id",
|
121
|
+
"showAs": "show_as",
|
122
|
+
"onlineMeeting": "online_meeting",
|
123
|
+
"onlineMeetingUrl": "online_meeting_url",
|
124
|
+
"isOnlineMeeting": "is_online_meeting",
|
125
|
+
"onlineMeetingProvider": "online_meeting_provider",
|
126
|
+
"allowNewTimeProposals": "allow_new_time_proposals",
|
127
|
+
"occurrenceId": "occurrence_id",
|
128
|
+
"isDraft": "is_draft",
|
129
|
+
"hideAttendees": "hide_attendees",
|
130
|
+
"responseStatus": "response_status",
|
131
|
+
"appId": "app_id",
|
132
|
+
"passwordCredentials": "password_credentials",
|
133
|
+
"iCalUId": "uid",
|
134
|
+
"body.contentType": "body.content_type",
|
135
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
from .calls import NumberOfCallKPI
|