|
1 | 1 | """ |
2 | | -Meeting finder: suggest optimal time slots and resolve rooms. |
| 2 | +Find meeting times and list rooms in the tenant. |
3 | 3 |
|
4 | | -Brings together three related patterns: |
5 | | - 1. Find available meeting times that work for all attendees. |
6 | | - 2. List room lists and rooms in the tenant. |
7 | | - 3. Get free/busy schedule for a room across a time window. |
8 | | -
|
9 | | -Useful for scheduling assistants, booking flows, and room |
10 | | -management tooling. |
11 | | -
|
12 | | -Requires delegated permissions: |
13 | | - Calendars.ReadWrite Create and manage events |
14 | | - MailboxSettings.Read Read user availability |
15 | | - Place.Read.All List rooms and room lists |
16 | | -
|
17 | | -https://learn.microsoft.com/en-us/graph/api/user-findmeetingtimes |
18 | | -https://learn.microsoft.com/en-us/graph/api/resources/place |
| 4 | +Requires delegated permissions Calendars.ReadWrite, MailboxSettings.Read, |
| 5 | +Place.Read.All. |
19 | 6 | """ |
20 | 7 |
|
21 | | -from datetime import datetime, timedelta, timezone |
| 8 | +from datetime import timedelta |
22 | 9 |
|
23 | 10 | from office365.graph_client import GraphClient |
24 | 11 | from tests import test_client_id, test_password, test_tenant, test_username |
|
27 | 14 | def main(): |
28 | 15 | client = GraphClient(tenant=test_tenant).with_username_and_password(test_client_id, test_username, test_password) |
29 | 16 |
|
30 | | - # -- Step 1: list room lists and rooms in the tenant -- |
31 | | - print("Places (room lists and rooms):") |
32 | | - all_places = client.places.get().execute_query() |
33 | | - for p in all_places: |
34 | | - print(f" {p.display_name:45s} type={p.entity_type_name}") |
35 | | - print() |
36 | | - |
37 | | - # Try to get room lists specifically |
38 | 17 | room_lists = client.places.room_lists.get().execute_query() |
39 | 18 | if room_lists: |
40 | | - print("Room lists:") |
41 | 19 | for rl in room_lists: |
42 | | - print(f" {rl.display_name:45s} {rl.email_address or ''}") |
43 | | - # Get rooms in this list |
| 20 | + print(f"Room list: {rl.display_name} ({rl.email_address or ''})") |
44 | 21 | try: |
45 | 22 | rooms = rl.rooms.get().execute_query() |
46 | 23 | for room in rooms: |
47 | | - print(f" ↳ {room.display_name:40s} {room.email_address or ''} ({room.capacity or '?'} seats)") |
| 24 | + print(f" {room.display_name} {room.email_address} (capacity: {room.capacity})") |
48 | 25 | except Exception: |
49 | 26 | pass |
50 | | - else: |
51 | | - print("(No room lists found — room list management may need Place.Read.All)") |
52 | | - |
53 | | - # -- Step 2: find meeting times for next week -- |
54 | | - now = datetime.now(timezone.utc) |
55 | | - start_time = now + timedelta(days=1) |
56 | | - end_time = now + timedelta(days=7) |
57 | 27 |
|
58 | | - # Collect room email addresses if available |
59 | | - attendee_smtp = ["meganb@contoso.onmicrosoft.com"] |
| 28 | + attendees = ["meganb@contoso.onmicrosoft.com"] |
60 | 29 | if room_lists: |
61 | | - for rl in room_lists[:1]: # first room list |
62 | | - try: |
63 | | - rooms = rl.rooms.get().execute_query() |
64 | | - attendee_smtp.extend(r.email_address for r in rooms[:2] if r.email_address) |
65 | | - except Exception: |
66 | | - pass |
67 | | - |
68 | | - print(f"\nFinding meeting times for: {', '.join(attendee_smtp)}") |
69 | | - print(f" Window: {start_time.strftime('%Y-%m-%d %H:%M')} — {end_time.strftime('%Y-%m-%d %H:%M')}") |
70 | | - |
71 | | - suggestions = client.me.find_meeting_times( |
72 | | - attendees=attendee_smtp, |
73 | | - meeting_duration=timedelta(hours=1), |
74 | | - max_candidates=5, |
| 30 | + try: |
| 31 | + rooms = room_lists[0].rooms.get().execute_query() |
| 32 | + attendees.extend(r.email_address for r in rooms[:2] if r.email_address) |
| 33 | + except Exception: |
| 34 | + pass |
| 35 | + |
| 36 | + result = client.me.find_meeting_times( |
| 37 | + attendees=attendees, meeting_duration=timedelta(hours=1), max_candidates=5 |
75 | 38 | ).execute_query() |
76 | | - |
77 | | - if suggestions and hasattr(suggestions, "value") and suggestions.value: |
78 | | - print(f"\nSuggested times ({len(suggestions.value)} candidate(s)):\n") |
79 | | - # The value is a MeetingTimeSuggestionsResult |
80 | | - result = suggestions.value |
81 | | - if hasattr(result, "meeting_time_suggestions") and result.meeting_time_suggestions: |
82 | | - for s in result.meeting_time_suggestions: |
83 | | - slot = s.meeting_time_slot |
84 | | - print( |
85 | | - f" {slot.start.strftime('%Y-%m-%d %H:%M')} — " |
86 | | - f"{slot.end.strftime('%H:%M')} (confidence: {s.confidence})" |
87 | | - ) |
88 | | - if hasattr(s, "locations") and s.locations: |
89 | | - for loc in s.locations: |
90 | | - print(f" Location: {loc.display_name}") |
91 | | - else: |
92 | | - print(" (no detailed suggestions — check permissions)") |
93 | | - else: |
94 | | - print(" No meeting suggestions returned.") |
| 39 | + if result and result.value and result.value.meeting_time_suggestions: |
| 40 | + for s in result.value.meeting_time_suggestions: |
| 41 | + slot = s.meeting_time_slot |
| 42 | + print(f" {slot.start} — {slot.end} (confidence: {s.confidence})") |
95 | 43 |
|
96 | 44 |
|
97 | 45 | if __name__ == "__main__": |
|
0 commit comments