Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,787 +2,132 @@ import os
|
|
2 |
import streamlit as st
|
3 |
import requests
|
4 |
import msal
|
5 |
-
import urllib.parse
|
6 |
-
import base64
|
7 |
-
import hashlib
|
8 |
import secrets
|
9 |
|
10 |
-
#
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
# ๐ฏ Define the scopes your app will need
|
19 |
-
#SCOPES = ['User.Read', 'Calendars.ReadWrite', 'Mail.ReadWrite', 'Notes.ReadWrite.All', 'Files.ReadWrite.All']
|
20 |
-
SCOPES = ['User.Read']
|
21 |
-
|
22 |
-
# New function to generate PKCE code verifier and challenge
|
23 |
-
def generate_pkce_codes():
|
24 |
-
code_verifier = secrets.token_urlsafe(128)[:128]
|
25 |
-
code_challenge = base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest()).decode().rstrip('=')
|
26 |
-
return code_verifier, code_challenge
|
27 |
|
|
|
28 |
def get_msal_app():
|
29 |
return msal.ConfidentialClientApplication(
|
30 |
-
client_id=
|
31 |
-
client_credential=
|
32 |
-
authority=
|
33 |
-
)
|
34 |
-
|
35 |
-
def get_msal_app2():
|
36 |
-
return msal.PublicClientApplication(
|
37 |
-
client_id=APPLICATION_ID_KEY,
|
38 |
-
authority=AUTHORITY_URL
|
39 |
)
|
40 |
|
41 |
-
#
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
st.session_state['
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
client_instance = get_msal_app()
|
52 |
-
|
53 |
-
|
54 |
-
def get_access_token(code=None):
|
55 |
-
client_instance = get_msal_app()
|
56 |
-
|
57 |
-
try:
|
58 |
-
if code:
|
59 |
-
# Initial token acquisition
|
60 |
-
result = client_instance.acquire_token_by_authorization_code(
|
61 |
-
code=code,
|
62 |
-
scopes=SCOPES,
|
63 |
-
redirect_uri=REDIRECT_URI
|
64 |
-
)
|
65 |
-
elif 'refresh_token' in st.session_state:
|
66 |
-
# Token refresh
|
67 |
-
result = client_instance.acquire_token_by_refresh_token(
|
68 |
-
refresh_token=st.session_state['refresh_token'],
|
69 |
-
scopes=SCOPES
|
70 |
-
)
|
71 |
-
else:
|
72 |
-
raise Exception("No authorization code or refresh token available")
|
73 |
-
|
74 |
-
if 'access_token' in result:
|
75 |
-
st.session_state['access_token'] = result['access_token']
|
76 |
-
if 'refresh_token' in result:
|
77 |
-
st.session_state['refresh_token'] = result['refresh_token']
|
78 |
-
return result['access_token']
|
79 |
-
else:
|
80 |
-
error_description = result.get('error_description', 'No error description provided')
|
81 |
-
raise Exception(f"Error acquiring token: {error_description}")
|
82 |
-
except Exception as e:
|
83 |
-
st.error(f"Exception in get_access_token: {str(e)}")
|
84 |
-
raise
|
85 |
-
|
86 |
-
def get_access_token4(code):
|
87 |
-
client_instance = get_msal_app()
|
88 |
-
|
89 |
-
try:
|
90 |
-
result = client_instance.acquire_token_by_authorization_code(
|
91 |
-
code=code,
|
92 |
-
scopes=SCOPES,
|
93 |
-
redirect_uri=REDIRECT_URI
|
94 |
-
)
|
95 |
-
|
96 |
-
if 'access_token' in result:
|
97 |
-
return result['access_token']
|
98 |
-
else:
|
99 |
-
error_description = result.get('error_description', 'No error description provided')
|
100 |
-
raise Exception(f"Error acquiring token: {error_description}")
|
101 |
-
except Exception as e:
|
102 |
-
st.error(f"Exception in get_access_token: {str(e)}")
|
103 |
-
raise
|
104 |
-
|
105 |
-
def get_access_token3(code):
|
106 |
-
client_instance = get_msal_app()
|
107 |
-
|
108 |
-
st.write("Debug: MSAL App Configuration:")
|
109 |
-
st.write(f"Client ID: {APPLICATION_ID_KEY[:5]}...")
|
110 |
-
st.write(f"Authority: {AUTHORITY_URL}")
|
111 |
-
st.write(f"Redirect URI: {REDIRECT_URI}")
|
112 |
-
|
113 |
-
try:
|
114 |
-
# Without code_verifier (for testing)
|
115 |
-
result = client_instance.acquire_token_by_authorization_code(
|
116 |
-
code=code,
|
117 |
-
scopes=SCOPES,
|
118 |
-
redirect_uri=REDIRECT_URI
|
119 |
-
)
|
120 |
-
|
121 |
-
if 'access_token' in result:
|
122 |
-
return result['access_token']
|
123 |
-
else:
|
124 |
-
error_description = result.get('error_description', 'No error description provided')
|
125 |
-
raise Exception(f"Error acquiring token: {error_description}")
|
126 |
-
except Exception as e:
|
127 |
-
st.error(f"Exception in get_access_token: {str(e)}")
|
128 |
-
raise
|
129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
|
|
|
|
|
|
|
131 |
|
132 |
-
#
|
133 |
-
|
134 |
-
client_instance = get_msal_app()
|
135 |
-
|
136 |
-
st.write("Debug: MSAL App Configuration:")
|
137 |
-
st.write(f"Client ID: {APPLICATION_ID_KEY[:5]}...")
|
138 |
-
st.write(f"Authority: {AUTHORITY_URL}")
|
139 |
-
st.write(f"Redirect URI: {REDIRECT_URI}")
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
code=code,
|
145 |
-
scopes=SCOPES,
|
146 |
-
redirect_uri=REDIRECT_URI,
|
147 |
-
code_verifier=code_verifier # Ensure this is passed only if PKCE is used
|
148 |
-
)
|
149 |
-
|
150 |
-
if 'access_token' in result:
|
151 |
-
return result['access_token']
|
152 |
-
else:
|
153 |
-
error_description = result.get('error_description', 'No error description provided')
|
154 |
-
raise Exception(f"Error acquiring token: {error_description}")
|
155 |
-
except Exception as e:
|
156 |
-
st.error(f"Exception in get_access_token: {str(e)}")
|
157 |
-
raise
|
158 |
-
|
159 |
-
def clear_query_params():
|
160 |
-
# Get the current URL without query parameters
|
161 |
-
base_url = st.get_page_config().base_url_path
|
162 |
-
st.write(f'<meta http-equiv="refresh" content="0; url={base_url}">', unsafe_allow_html=True)
|
163 |
-
st.stop()
|
164 |
-
|
165 |
-
def process_query_params():
|
166 |
-
query_params = st.query_params
|
167 |
-
|
168 |
-
if 'error' in query_params:
|
169 |
-
error = query_params.get('error')
|
170 |
-
error_description = query_params.get('error_description', 'No description provided')
|
171 |
-
st.error(f"Authentication Error: {error}")
|
172 |
-
st.error(f"Error Description: {urllib.parse.unquote(error_description)}")
|
173 |
-
st.session_state.clear()
|
174 |
-
st.rerun()
|
175 |
-
|
176 |
-
if 'code' in query_params and 'state' in query_params:
|
177 |
-
received_state = query_params.get('state')
|
178 |
-
if received_state != st.session_state.get('auth_state'):
|
179 |
st.error("Invalid state parameter. Please try logging in again.")
|
180 |
st.session_state.clear()
|
181 |
st.rerun()
|
182 |
-
|
183 |
-
code = query_params.get('code')
|
184 |
try:
|
185 |
-
token =
|
186 |
-
st.session_state['
|
187 |
st.success("Successfully authenticated!")
|
188 |
-
# Clear the URL parameters
|
189 |
-
st.experimental_set_query_params()
|
190 |
st.rerun()
|
191 |
except Exception as e:
|
192 |
-
st.error(f"
|
193 |
st.session_state.clear()
|
194 |
-
st.rerun()
|
195 |
-
|
196 |
-
def process_query_params3():
|
197 |
-
query_params = st.query_params
|
198 |
-
st.write("Debug: All query parameters:", query_params)
|
199 |
-
|
200 |
-
if 'error' in query_params:
|
201 |
-
error = query_params.get('error')
|
202 |
-
error_description = query_params.get('error_description', 'No description provided')
|
203 |
-
st.error(f"Authentication Error: {error}")
|
204 |
-
st.error(f"Error Description: {urllib.parse.unquote(error_description)}")
|
205 |
-
st.stop()
|
206 |
-
|
207 |
-
if 'code' in query_params:
|
208 |
-
code = query_params.get('code')
|
209 |
-
st.write('๐ Authorization Code Obtained:', code[:10] + '...')
|
210 |
-
|
211 |
-
try:
|
212 |
-
access_token = get_access_token(code)
|
213 |
-
st.session_state['access_token'] = access_token
|
214 |
-
st.success("Access token acquired successfully!")
|
215 |
-
# Redirect to clear query params
|
216 |
-
st.rerun()
|
217 |
-
except Exception as e:
|
218 |
-
st.error(f"Error acquiring access token: {str(e)}")
|
219 |
-
st.stop()
|
220 |
-
|
221 |
-
# Handle other query parameters as needed
|
222 |
-
if 'q' in query_params or 'query' in query_params:
|
223 |
-
query = query_params.get('q') or query_params.get('query')
|
224 |
-
if query:
|
225 |
-
# Process the query
|
226 |
-
pass
|
227 |
-
|
228 |
-
if 'action' in query_params:
|
229 |
-
action = query_params.get('action')
|
230 |
-
if action == 'show_message':
|
231 |
-
st.success("Showing a message because 'action=show_message' was found in the URL.")
|
232 |
-
elif action == 'clear':
|
233 |
-
# Instead of clearing query params, we'll redirect
|
234 |
-
st.rerun()
|
235 |
-
|
236 |
-
def process_query_params2():
|
237 |
-
# โ๏ธq= Run ArXiv search from query parameters
|
238 |
-
try:
|
239 |
-
query_params = st.query_params
|
240 |
-
st.write("Debug: All query parameters:", query_params)
|
241 |
-
|
242 |
-
if 'error' in query_params:
|
243 |
-
error = query_params.get('error')
|
244 |
-
error_description = query_params.get('error_description', 'No description provided')
|
245 |
-
st.error(f"Authentication Error: {error}")
|
246 |
-
st.error(f"Error Description: {urllib.parse.unquote(error_description)}")
|
247 |
-
st.stop()
|
248 |
-
|
249 |
-
if 'code' in query_params:
|
250 |
-
code = query_params.get('code')
|
251 |
-
st.write('๐ Authorization Code Obtained:', code[:10] + '...')
|
252 |
-
|
253 |
-
try:
|
254 |
-
code_verifier = st.session_state.get('code_verifier')
|
255 |
-
if not code_verifier:
|
256 |
-
st.error("Code verifier not found in session state.")
|
257 |
-
st.stop()
|
258 |
-
|
259 |
-
#access_token = get_access_token(code, code_verifier)
|
260 |
-
access_token = get_access_token(code)
|
261 |
-
|
262 |
-
|
263 |
-
st.session_state['access_token'] = access_token
|
264 |
-
st.success("Access token acquired successfully!")
|
265 |
-
# Clear the code from the URL
|
266 |
-
st.experimental_set_query_params()
|
267 |
-
st.rerun()
|
268 |
-
except Exception as e:
|
269 |
-
st.error(f"Error acquiring access token: {str(e)}")
|
270 |
-
st.stop()
|
271 |
-
else:
|
272 |
-
st.warning("No authorization code found in the query parameters.")
|
273 |
-
|
274 |
-
|
275 |
-
query = (query_params.get('q') or query_params.get('query') or [''])
|
276 |
-
if len(query) > 1:
|
277 |
-
#result = search_arxiv(query)
|
278 |
-
#result2 = search_glossary(result)
|
279 |
-
|
280 |
-
filesearch = PromptPrefix + query
|
281 |
-
st.markdown(filesearch)
|
282 |
-
process_text(filesearch)
|
283 |
-
except:
|
284 |
-
st.markdown(' ')
|
285 |
-
|
286 |
-
if 'action' in st.query_params:
|
287 |
-
action = st.query_params()['action'][0] # Get the first (or only) 'action' parameter
|
288 |
-
if action == 'show_message':
|
289 |
-
st.success("Showing a message because 'action=show_message' was found in the URL.")
|
290 |
-
elif action == 'clear':
|
291 |
-
clear_query_params()
|
292 |
-
#st.rerun()
|
293 |
-
|
294 |
-
if 'query' in st.query_params:
|
295 |
-
query = st.query_params['query'][0] # Get the query parameter
|
296 |
-
# Display content or image based on the query
|
297 |
-
display_content_or_image(query)
|
298 |
-
|
299 |
-
def get_user_info(access_token):
|
300 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
301 |
-
response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers)
|
302 |
-
if response.status_code == 200:
|
303 |
-
return response.json()
|
304 |
-
else:
|
305 |
-
raise Exception(f"Failed to fetch user info: {response.status_code} - {response.text}")
|
306 |
-
|
307 |
-
def generate_state():
|
308 |
-
state = secrets.token_urlsafe(32)
|
309 |
-
st.session_state['auth_state'] = state
|
310 |
-
return state
|
311 |
|
312 |
-
def initiate_auth_flow():
|
313 |
-
client_instance = get_msal_app()
|
314 |
-
auth_url = client_instance.get_authorization_request_url(
|
315 |
-
scopes=SCOPES,
|
316 |
-
redirect_uri=REDIRECT_URI,
|
317 |
-
state=generate_state()
|
318 |
-
)
|
319 |
-
st.write('๐ Please [click here]({}) to log in and authorize the app.'.format(auth_url))
|
320 |
-
|
321 |
-
def is_token_valid(token):
|
322 |
if not token:
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
# If not, initiate the login process
|
342 |
-
initiate_auth_flow()
|
343 |
-
st.stop()
|
344 |
-
|
345 |
-
# If we have a valid token, proceed with the app
|
346 |
-
access_token = st.session_state['access_token']
|
347 |
-
|
348 |
-
|
349 |
-
#added
|
350 |
-
if 'access_token' not in st.session_state:
|
351 |
-
client_instance = get_msal_app()
|
352 |
-
authorization_url = client_instance.get_authorization_request_url(
|
353 |
-
scopes=SCOPES,
|
354 |
-
redirect_uri=REDIRECT_URI
|
355 |
-
)
|
356 |
-
st.write('๐ Please [click here]({}) to log in and authorize the app.'.format(authorization_url))
|
357 |
-
st.stop()
|
358 |
-
|
359 |
-
try:
|
360 |
-
# Attempt to use the access token
|
361 |
-
user_info = get_user_info(st.session_state['access_token'])
|
362 |
-
except Exception as e:
|
363 |
-
if "token has expired" in str(e).lower():
|
364 |
-
# Token expired, attempt to refresh
|
365 |
-
try:
|
366 |
-
new_token = get_access_token() # This will use the refresh token if available
|
367 |
-
st.session_state['access_token'] = new_token
|
368 |
-
user_info = get_user_info(new_token)
|
369 |
-
except Exception as refresh_error:
|
370 |
-
st.error("Failed to refresh token. Please log in again.")
|
371 |
-
st.session_state.clear()
|
372 |
-
st.rerun()
|
373 |
-
|
374 |
-
if 'access_token' not in st.session_state:
|
375 |
-
query_params = st.query_params
|
376 |
-
if 'code' in query_params:
|
377 |
-
code = query_params.get('code')
|
378 |
-
st.write('๐ Authorization Code Obtained:', code[:10] + '...')
|
379 |
-
|
380 |
-
try:
|
381 |
-
access_token = get_access_token(code)
|
382 |
-
st.session_state['access_token'] = access_token
|
383 |
-
st.success("Access token acquired successfully!")
|
384 |
-
# Instead of clearing query params, we'll redirect to remove them
|
385 |
-
st.rerun()
|
386 |
-
except Exception as e:
|
387 |
-
if "AADSTS70000" in str(e) and "code has expired" in str(e):
|
388 |
-
st.error("The authorization code has expired. Please log in again.")
|
389 |
-
st.session_state.pop('access_token', None)
|
390 |
-
# Redirect to clear query params
|
391 |
-
st.rerun()
|
392 |
-
else:
|
393 |
-
st.error(f"Error acquiring access token: {str(e)}")
|
394 |
-
st.stop()
|
395 |
-
else:
|
396 |
-
client_instance = get_msal_app()
|
397 |
-
authorization_url = client_instance.get_authorization_request_url(
|
398 |
-
scopes=SCOPES,
|
399 |
-
redirect_uri=REDIRECT_URI
|
400 |
-
)
|
401 |
-
st.write('๐ Please [click here]({}) to log in and authorize the app.'.format(authorization_url))
|
402 |
-
st.stop()
|
403 |
-
else:
|
404 |
-
# Rest of your code for authenticated users
|
405 |
-
pass
|
406 |
-
|
407 |
-
|
408 |
-
# ๐ Sidebar navigation
|
409 |
-
st.sidebar.title("Navigation")
|
410 |
-
menu = st.sidebar.radio("Go to", [
|
411 |
-
"1๏ธโฃ Dashboard",
|
412 |
-
"๐ Landing Page",
|
413 |
-
"๐
Upcoming Events",
|
414 |
-
"๐ Schedule",
|
415 |
-
"๐ Agenda",
|
416 |
-
"๐ Event Details",
|
417 |
-
"โ Add Event",
|
418 |
-
"๐ Filter By"
|
419 |
-
])
|
420 |
-
|
421 |
-
# ๐ M365 Products with MS Graph Integration & AI Features
|
422 |
-
st.sidebar.title("๐ M365 Products")
|
423 |
-
st.sidebar.write("Toggle integration for each product:")
|
424 |
-
|
425 |
-
# ๐๏ธ Product Integration Toggles
|
426 |
-
products = {
|
427 |
-
"๐ง Outlook": {
|
428 |
-
"ai_capabilities": "Copilot for enhanced email writing, calendar management, and scheduling.",
|
429 |
-
"graph_api": "Access to mail, calendar, contacts, and events."
|
430 |
-
},
|
431 |
-
"๐ OneNote": {
|
432 |
-
"ai_capabilities": "Content suggestion, note organization, and OCR for extracting text from images.",
|
433 |
-
"graph_api": "Manage notebooks, sections, and pages."
|
434 |
-
},
|
435 |
-
"๐ Excel": {
|
436 |
-
"ai_capabilities": "Copilot for advanced data analysis, data insights, and formula generation.",
|
437 |
-
"graph_api": "Create and manage worksheets, tables, charts, and workbooks."
|
438 |
-
},
|
439 |
-
"๐ Word": {
|
440 |
-
"ai_capabilities": "Copilot for document drafting, summarization, and grammar improvements.",
|
441 |
-
"graph_api": "Document content access, templates, and file management."
|
442 |
-
},
|
443 |
-
"๐๏ธ SharePoint": {
|
444 |
-
"ai_capabilities": "Intelligent search, document tagging, and metadata extraction.",
|
445 |
-
"graph_api": "Access to sites, lists, files, and document libraries."
|
446 |
-
},
|
447 |
-
"๐
Teams": {
|
448 |
-
"ai_capabilities": "Copilot for meeting summaries, transcription, and chat suggestions.",
|
449 |
-
"graph_api": "Manage chats, teams, channels, and meetings."
|
450 |
-
},
|
451 |
-
"๐ฌ Viva": {
|
452 |
-
"ai_capabilities": "Personalized learning insights, well-being insights, and productivity suggestions.",
|
453 |
-
"graph_api": "Access to user analytics and learning modules."
|
454 |
-
},
|
455 |
-
"๐ Power Platform": {
|
456 |
-
"ai_capabilities": "Automation with AI Builder, data insights, and custom AI models.",
|
457 |
-
"graph_api": "Automation workflows, app creation, and data visualization."
|
458 |
-
},
|
459 |
-
"๐ง Copilot": {
|
460 |
-
"ai_capabilities": "Embedded across Word, Excel, Outlook, Teams, and more for AI-driven productivity.",
|
461 |
-
"graph_api": "Underpins Copilot's access to data and integrations."
|
462 |
-
},
|
463 |
-
"๐๏ธ OneDrive": {
|
464 |
-
"ai_capabilities": "Intelligent file organization and search.",
|
465 |
-
"graph_api": "File and folder access, sharing, and metadata."
|
466 |
-
},
|
467 |
-
"๐ก PowerPoint": {
|
468 |
-
"ai_capabilities": "Design suggestions, presentation summarization, and speaker coaching.",
|
469 |
-
"graph_api": "Presentation creation, slide management, and templates."
|
470 |
-
},
|
471 |
-
"๐ Microsoft Bookings": {
|
472 |
-
"ai_capabilities": "Automated scheduling and reminders.",
|
473 |
-
"graph_api": "Booking calendars, services, and appointment details."
|
474 |
-
},
|
475 |
-
"๐ Loop": {
|
476 |
-
"ai_capabilities": "Real-time collaboration and content suggestions.",
|
477 |
-
"graph_api": "Access to shared workspaces and collaborative pages."
|
478 |
-
},
|
479 |
-
"๐ฃ๏ธ Translator": {
|
480 |
-
"ai_capabilities": "Real-time language translation and text-to-speech.",
|
481 |
-
"graph_api": "Integrated into communication and translation services."
|
482 |
-
},
|
483 |
-
"๐ To Do & Planner": {
|
484 |
-
"ai_capabilities": "Task prioritization and smart reminders.",
|
485 |
-
"graph_api": "Task creation, management, and synchronization."
|
486 |
-
},
|
487 |
-
"๐ Azure OpenAI Service": {
|
488 |
-
"ai_capabilities": "Access to GPT models for custom AI implementations.",
|
489 |
-
"graph_api": "Used indirectly for building custom AI models into workflows."
|
490 |
-
}
|
491 |
-
}
|
492 |
-
|
493 |
-
# ๐ณ๏ธ Create toggles for each product
|
494 |
-
selected_products = {}
|
495 |
-
for product, info in products.items():
|
496 |
-
selected = st.sidebar.checkbox(product)
|
497 |
-
if selected:
|
498 |
-
st.sidebar.write(f"**AI Capabilities:** {info['ai_capabilities']}")
|
499 |
-
st.sidebar.write(f"**Graph API:** {info['graph_api']}")
|
500 |
-
selected_products[product] = True
|
501 |
-
|
502 |
-
# ๐ Authentication
|
503 |
-
if 'access_token' not in st.session_state:
|
504 |
-
# ๐ต๏ธโโ๏ธ Check for authorization code in query parameters
|
505 |
-
query_params = st.query_params
|
506 |
-
query = (query_params.get('code'))
|
507 |
-
#if len(query) > 1:
|
508 |
-
|
509 |
-
#st.write('Parsing query ' + query_params )
|
510 |
-
if 'code' in query_params:
|
511 |
-
#code = query_params['code'][0]
|
512 |
-
code = query_params.get('code')
|
513 |
-
|
514 |
-
st.write('๐Access Code Obtained from MS Graph Redirect๐!:' + code)
|
515 |
-
|
516 |
-
st.write('๐ Acquiring access token from redirect URL for code parameter.')
|
517 |
-
access_token = get_access_token(code)
|
518 |
-
|
519 |
-
st.session_state['access_token'] = access_token # ๐ Save it for later
|
520 |
-
|
521 |
-
st.rerun() # Reload the app to clear the code from URL
|
522 |
-
else:
|
523 |
-
# ๐ข Prompt user to log in
|
524 |
-
client_instance = get_msal_app()
|
525 |
-
authorization_url = client_instance.get_authorization_request_url(
|
526 |
-
scopes=SCOPES,
|
527 |
-
redirect_uri=REDIRECT_URI
|
528 |
-
)
|
529 |
-
st.write('๐ Please [click here]({}) to log in and authorize the app.'.format(authorization_url))
|
530 |
-
st.stop()
|
531 |
-
|
532 |
-
|
533 |
-
else:
|
534 |
-
# ๐ฅณ User is authenticated, proceed with the app
|
535 |
-
access_token = st.session_state['access_token']
|
536 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
537 |
-
|
538 |
-
# ๐ค Greet the user
|
539 |
-
response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers)
|
540 |
-
if response.status_code == 200:
|
541 |
-
user_info = response.json()
|
542 |
-
st.sidebar.write(f"๐ Hello, {user_info['displayName']}!")
|
543 |
-
else:
|
544 |
-
st.error('Failed to fetch user info.')
|
545 |
-
st.write(response.text)
|
546 |
-
|
547 |
-
# ๐๏ธ Handle menu options
|
548 |
-
if menu == "1๏ธโฃ Dashboard with Widgets":
|
549 |
-
st.header("1๏ธโฃ Dashboard with Widgets")
|
550 |
-
st.write("Widgets will be displayed here. ๐๏ธ")
|
551 |
-
# Add your widgets here
|
552 |
-
|
553 |
-
elif menu == "๐ Landing Page":
|
554 |
-
st.header("๐ Landing Page")
|
555 |
-
st.write("Welcome to the app! ๐ฅณ")
|
556 |
-
# Add landing page content here
|
557 |
-
|
558 |
-
elif menu == "๐
Upcoming Events":
|
559 |
-
st.header("๐
Upcoming Events")
|
560 |
-
events = get_upcoming_events(access_token)
|
561 |
-
for event in events:
|
562 |
-
st.write(f"๐ {event['subject']} on {event['start']['dateTime']}")
|
563 |
-
# Display upcoming events
|
564 |
-
|
565 |
-
elif menu == "๐ Schedule":
|
566 |
-
st.header("๐ Schedule")
|
567 |
-
schedule = get_schedule(access_token)
|
568 |
-
st.write(schedule)
|
569 |
-
# Display schedule
|
570 |
-
|
571 |
-
elif menu == "๐ Agenda":
|
572 |
-
st.header("๐ Agenda")
|
573 |
-
st.write("Your agenda for today. ๐")
|
574 |
-
# Display agenda
|
575 |
-
|
576 |
-
elif menu == "๐ Event Details":
|
577 |
-
st.header("๐ Event Details")
|
578 |
-
event_id = st.text_input("Enter Event ID")
|
579 |
-
if event_id:
|
580 |
-
event_details = get_event_details(access_token, event_id)
|
581 |
-
st.write(event_details)
|
582 |
-
# Display event details based on ID
|
583 |
-
|
584 |
-
elif menu == "โ Add Event":
|
585 |
-
st.header("โ Add Event")
|
586 |
-
event_subject = st.text_input("Event Subject")
|
587 |
-
event_start = st.date_input("Event Start Date")
|
588 |
-
event_start_time = st.time_input("Event Start Time")
|
589 |
-
event_end = st.date_input("Event End Date")
|
590 |
-
event_end_time = st.time_input("Event End Time")
|
591 |
-
if st.button("Add Event"):
|
592 |
-
event_details = {
|
593 |
-
"subject": event_subject,
|
594 |
-
"start": {
|
595 |
-
"dateTime": f"{event_start}T{event_start_time}",
|
596 |
-
"timeZone": "UTC"
|
597 |
-
},
|
598 |
-
"end": {
|
599 |
-
"dateTime": f"{event_end}T{event_end_time}",
|
600 |
-
"timeZone": "UTC"
|
601 |
-
}
|
602 |
-
}
|
603 |
-
add_event(access_token, event_details)
|
604 |
-
st.success("Event added successfully! ๐")
|
605 |
-
# Form to add new event
|
606 |
-
|
607 |
-
elif menu == "๐ Filter By":
|
608 |
-
st.header("๐ Filter Events")
|
609 |
-
filter_criteria = st.text_input("Enter filter criteria")
|
610 |
-
if filter_criteria:
|
611 |
-
filtered_events = filter_events(access_token, filter_criteria)
|
612 |
-
for event in filtered_events:
|
613 |
-
st.write(f"๐
{event['subject']} on {event['start']['dateTime']}")
|
614 |
-
# Filter events based on criteria
|
615 |
-
|
616 |
-
# ๐งฉ Handle selected products
|
617 |
-
if selected_products:
|
618 |
-
st.header("๐งฉ M365 Product Integrations")
|
619 |
-
for product in selected_products:
|
620 |
-
st.subheader(f"{product}")
|
621 |
-
# Call the function corresponding to the product
|
622 |
-
handle_product_integration(access_token, product)
|
623 |
-
else:
|
624 |
-
st.write("No products selected.")
|
625 |
-
pass
|
626 |
-
|
627 |
-
# ๐งฉ Function to handle product integration
|
628 |
-
def handle_product_integration(access_token, product):
|
629 |
-
if product == "๐ง Outlook":
|
630 |
-
st.write("Accessing Outlook data...")
|
631 |
-
# Implement Outlook integration
|
632 |
-
inbox_messages = get_outlook_messages(access_token)
|
633 |
-
st.write("Inbox Messages:")
|
634 |
-
for msg in inbox_messages:
|
635 |
-
st.write(f"โ๏ธ {msg['subject']}")
|
636 |
-
elif product == "๐ OneNote":
|
637 |
-
st.write("Accessing OneNote data...")
|
638 |
-
# Implement OneNote integration
|
639 |
-
notebooks = get_onenote_notebooks(access_token)
|
640 |
-
st.write("Notebooks:")
|
641 |
-
for notebook in notebooks:
|
642 |
-
st.write(f"๐ {notebook['displayName']}")
|
643 |
-
elif product == "๐ Excel":
|
644 |
-
st.write("Accessing Excel data...")
|
645 |
-
# Implement Excel integration
|
646 |
-
st.write("Excel integration is a placeholder.")
|
647 |
-
elif product == "๐ Word":
|
648 |
-
st.write("Accessing Word documents...")
|
649 |
-
# Implement Word integration
|
650 |
-
st.write("Word integration is a placeholder.")
|
651 |
-
elif product == "๐๏ธ SharePoint":
|
652 |
-
st.write("Accessing SharePoint sites...")
|
653 |
-
# Implement SharePoint integration
|
654 |
-
sites = get_sharepoint_sites(access_token)
|
655 |
-
st.write("Sites:")
|
656 |
-
for site in sites:
|
657 |
-
st.write(f"๐ {site['displayName']}")
|
658 |
-
elif product == "๐
Teams":
|
659 |
-
st.write("Accessing Teams data...")
|
660 |
-
# Implement Teams integration
|
661 |
-
teams = get_user_teams(access_token)
|
662 |
-
st.write("Teams:")
|
663 |
-
for team in teams:
|
664 |
-
st.write(f"๐ฅ {team['displayName']}")
|
665 |
-
# Add additional product integrations as needed
|
666 |
-
else:
|
667 |
-
st.write(f"No integration implemented for {product}.")
|
668 |
-
|
669 |
-
def get_outlook_messages():
|
670 |
-
messages = make_api_call('me/messages?$top=5')
|
671 |
-
if messages:
|
672 |
-
return messages.get('value', [])
|
673 |
-
return []
|
674 |
-
|
675 |
-
|
676 |
-
# ๐จ Function to get Outlook messages
|
677 |
-
def get_outlook_messages2(access_token):
|
678 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
679 |
-
response = requests.get('https://graph.microsoft.com/v1.0/me/messages?$top=5', headers=headers)
|
680 |
-
if response.status_code == 200:
|
681 |
-
messages = response.json().get('value', [])
|
682 |
-
return messages
|
683 |
-
else:
|
684 |
-
st.error('Failed to fetch messages.')
|
685 |
-
st.write(response.text)
|
686 |
-
return []
|
687 |
-
|
688 |
-
# ๐ Function to get OneNote notebooks
|
689 |
-
def get_onenote_notebooks(access_token):
|
690 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
691 |
-
response = requests.get('https://graph.microsoft.com/v1.0/me/onenote/notebooks', headers=headers)
|
692 |
-
if response.status_code == 200:
|
693 |
-
notebooks = response.json().get('value', [])
|
694 |
-
return notebooks
|
695 |
-
else:
|
696 |
-
st.error('Failed to fetch notebooks.')
|
697 |
-
st.write(response.text)
|
698 |
-
return []
|
699 |
-
|
700 |
-
# ๐ Function to get SharePoint sites
|
701 |
-
def get_sharepoint_sites(access_token):
|
702 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
703 |
-
response = requests.get('https://graph.microsoft.com/v1.0/sites?search=*', headers=headers)
|
704 |
-
if response.status_code == 200:
|
705 |
-
sites = response.json().get('value', [])
|
706 |
-
return sites
|
707 |
-
else:
|
708 |
-
st.error('Failed to fetch sites.')
|
709 |
-
st.write(response.text)
|
710 |
-
return []
|
711 |
-
|
712 |
-
# ๐ฅ Function to get user's Teams
|
713 |
-
def get_user_teams(access_token):
|
714 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
715 |
-
response = requests.get('https://graph.microsoft.com/v1.0/me/joinedTeams', headers=headers)
|
716 |
-
if response.status_code == 200:
|
717 |
-
teams = response.json().get('value', [])
|
718 |
-
return teams
|
719 |
-
else:
|
720 |
-
st.error('Failed to fetch teams.')
|
721 |
-
st.write(response.text)
|
722 |
-
return []
|
723 |
-
|
724 |
-
def get_upcoming_events():
|
725 |
-
events = make_api_call('me/events?$orderby=start/dateTime&$top=10')
|
726 |
-
if events:
|
727 |
-
return events.get('value', [])
|
728 |
-
return []
|
729 |
-
|
730 |
-
|
731 |
-
# ๐
Function to get upcoming events
|
732 |
-
def get_upcoming_events2(access_token):
|
733 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
734 |
-
response = requests.get('https://graph.microsoft.com/v1.0/me/events?$orderby=start/dateTime&$top=10', headers=headers)
|
735 |
-
if response.status_code == 200:
|
736 |
-
events = response.json().get('value', [])
|
737 |
-
return events
|
738 |
-
else:
|
739 |
-
st.error('Failed to fetch upcoming events.')
|
740 |
-
st.write(response.text)
|
741 |
-
return []
|
742 |
-
|
743 |
-
# ๐ Function to get schedule (Placeholder)
|
744 |
-
def get_schedule(access_token):
|
745 |
-
# Implement API call to get schedule
|
746 |
-
return "๐ Your schedule goes here."
|
747 |
-
|
748 |
-
# โ Function to add a new event
|
749 |
-
def add_event(access_token, event_details):
|
750 |
-
headers = {
|
751 |
-
'Authorization': 'Bearer ' + access_token,
|
752 |
-
'Content-Type': 'application/json'
|
753 |
-
}
|
754 |
-
response = requests.post('https://graph.microsoft.com/v1.0/me/events', headers=headers, json=event_details)
|
755 |
-
if response.status_code == 201:
|
756 |
-
st.success('Event created successfully! ๐')
|
757 |
-
else:
|
758 |
-
st.error('Failed to create event.')
|
759 |
-
st.write(response.text)
|
760 |
-
|
761 |
-
# ๐ Function to get event details
|
762 |
-
def get_event_details(access_token, event_id):
|
763 |
-
headers = {'Authorization': 'Bearer ' + access_token}
|
764 |
-
response = requests.get(f'https://graph.microsoft.com/v1.0/me/events/{event_id}', headers=headers)
|
765 |
-
if response.status_code == 200:
|
766 |
-
event = response.json()
|
767 |
-
return event
|
768 |
-
else:
|
769 |
-
st.error('Failed to fetch event details.')
|
770 |
-
st.write(response.text)
|
771 |
-
return {}
|
772 |
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
785 |
|
786 |
-
# ๐ Run the main function
|
787 |
if __name__ == "__main__":
|
788 |
-
main()
|
|
|
2 |
import streamlit as st
|
3 |
import requests
|
4 |
import msal
|
|
|
|
|
|
|
5 |
import secrets
|
6 |
|
7 |
+
# Configuration
|
8 |
+
APPLICATION_ID = os.getenv('APPLICATION_ID_KEY')
|
9 |
+
CLIENT_SECRET = os.getenv('CLIENT_SECRET_KEY')
|
10 |
+
AUTHORITY = 'https://login.microsoftonline.com/common'
|
11 |
+
REDIRECT_URI = 'https://huggingface.co/spaces/awacke1/MSGraphAPI'
|
12 |
+
SCOPES = ['User.Read', 'Calendars.ReadWrite', 'Mail.ReadWrite']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
+
# MSAL setup
|
15 |
def get_msal_app():
|
16 |
return msal.ConfidentialClientApplication(
|
17 |
+
client_id=APPLICATION_ID,
|
18 |
+
client_credential=CLIENT_SECRET,
|
19 |
+
authority=AUTHORITY
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
)
|
21 |
|
22 |
+
# Authentication functions
|
23 |
+
def generate_auth_url():
|
24 |
+
msal_app = get_msal_app()
|
25 |
+
state = secrets.token_urlsafe(32)
|
26 |
+
st.session_state['auth_state'] = state
|
27 |
+
return msal_app.get_authorization_request_url(
|
28 |
+
scopes=SCOPES,
|
29 |
+
redirect_uri=REDIRECT_URI,
|
30 |
+
state=state
|
31 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
+
def get_token_from_code(code):
|
34 |
+
msal_app = get_msal_app()
|
35 |
+
result = msal_app.acquire_token_by_authorization_code(
|
36 |
+
code=code,
|
37 |
+
scopes=SCOPES,
|
38 |
+
redirect_uri=REDIRECT_URI
|
39 |
+
)
|
40 |
+
if 'access_token' in result:
|
41 |
+
return result
|
42 |
+
else:
|
43 |
+
raise Exception(f"Error acquiring token: {result.get('error_description')}")
|
44 |
+
|
45 |
+
def get_token_from_cache():
|
46 |
+
if 'token_cache' in st.session_state:
|
47 |
+
msal_app = get_msal_app()
|
48 |
+
accounts = msal_app.get_accounts()
|
49 |
+
if accounts:
|
50 |
+
result = msal_app.acquire_token_silent(SCOPES, account=accounts[0])
|
51 |
+
if result:
|
52 |
+
return result
|
53 |
+
return None
|
54 |
+
|
55 |
+
# API call function
|
56 |
+
def make_api_call(endpoint, token):
|
57 |
+
headers = {'Authorization': f'Bearer {token}'}
|
58 |
+
response = requests.get(f'https://graph.microsoft.com/v1.0/{endpoint}', headers=headers)
|
59 |
+
if response.status_code == 200:
|
60 |
+
return response.json()
|
61 |
+
else:
|
62 |
+
st.error(f"API call failed: {response.status_code} - {response.text}")
|
63 |
+
return None
|
64 |
|
65 |
+
# Main application
|
66 |
+
def main():
|
67 |
+
st.title("๐ฆ MS Graph API Integration")
|
68 |
|
69 |
+
# Check for authentication
|
70 |
+
token = get_token_from_cache()
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
+
if 'code' in st.query_params:
|
73 |
+
state = st.query_params.get('state')
|
74 |
+
if state != st.session_state.get('auth_state'):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
st.error("Invalid state parameter. Please try logging in again.")
|
76 |
st.session_state.clear()
|
77 |
st.rerun()
|
78 |
+
|
|
|
79 |
try:
|
80 |
+
token = get_token_from_code(st.query_params['code'])
|
81 |
+
st.session_state['token_cache'] = token
|
82 |
st.success("Successfully authenticated!")
|
|
|
|
|
83 |
st.rerun()
|
84 |
except Exception as e:
|
85 |
+
st.error(f"Authentication failed: {str(e)}")
|
86 |
st.session_state.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
if not token:
|
89 |
+
auth_url = generate_auth_url()
|
90 |
+
st.write("Please log in to continue:")
|
91 |
+
st.markdown(f"[Login with Microsoft]({auth_url})")
|
92 |
+
return
|
93 |
+
|
94 |
+
# User is authenticated, show the main app
|
95 |
+
st.sidebar.success("Authenticated successfully!")
|
96 |
+
|
97 |
+
# Display user info
|
98 |
+
user_info = make_api_call('me', token['access_token'])
|
99 |
+
if user_info:
|
100 |
+
st.sidebar.write(f"Welcome, {user_info.get('displayName', 'User')}!")
|
101 |
+
|
102 |
+
# App functionality
|
103 |
+
option = st.sidebar.selectbox(
|
104 |
+
"Choose a function",
|
105 |
+
["View Emails", "View Calendar", "View OneDrive Files"]
|
106 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
|
108 |
+
if option == "View Emails":
|
109 |
+
emails = make_api_call('me/messages?$top=10', token['access_token'])
|
110 |
+
if emails:
|
111 |
+
for email in emails['value']:
|
112 |
+
st.write(f"Subject: {email['subject']}")
|
113 |
+
st.write(f"From: {email['from']['emailAddress']['name']}")
|
114 |
+
st.write("---")
|
115 |
+
|
116 |
+
elif option == "View Calendar":
|
117 |
+
events = make_api_call('me/events?$top=10', token['access_token'])
|
118 |
+
if events:
|
119 |
+
for event in events['value']:
|
120 |
+
st.write(f"Event: {event['subject']}")
|
121 |
+
st.write(f"Start: {event['start']['dateTime']}")
|
122 |
+
st.write("---")
|
123 |
+
|
124 |
+
elif option == "View OneDrive Files":
|
125 |
+
files = make_api_call('me/drive/root/children', token['access_token'])
|
126 |
+
if files:
|
127 |
+
for file in files['value']:
|
128 |
+
st.write(f"File: {file['name']}")
|
129 |
+
st.write(f"Type: {file['file']['mimeType'] if 'file' in file else 'Folder'}")
|
130 |
+
st.write("---")
|
131 |
|
|
|
132 |
if __name__ == "__main__":
|
133 |
+
main()
|