wbintegrator_office365 1.43.1__py2.py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|