Skip to content

Commit 3510d9f

Browse files
committed
feat: add Duration type for Edm.Duration, fix findMeetingTimes with proper AttendeeBase and camelCase key
1 parent bbc780e commit 3510d9f

3 files changed

Lines changed: 46 additions & 26 deletions

File tree

examples/outlook/calendars/meeting_finder.py

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,15 @@
1414
def main():
1515
client = GraphClient(tenant=test_tenant).with_username_and_password(test_client_id, test_username, test_password)
1616

17-
room_lists = client.places.room_lists.get().execute_query()
18-
if room_lists:
19-
for rl in room_lists:
20-
print(f"Room list: {rl.display_name} ({rl.email_address or ''})")
21-
try:
22-
rooms = rl.rooms.get().execute_query()
23-
for room in rooms:
24-
print(f" {room.display_name} {room.email_address} (capacity: {room.capacity})")
25-
except Exception:
26-
pass
27-
2817
attendees = ["meganb@contoso.onmicrosoft.com"]
29-
if room_lists:
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
3518

3619
result = client.me.find_meeting_times(
3720
attendees=attendees, meeting_duration=timedelta(hours=1), max_candidates=5
3821
).execute_query()
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})")
22+
23+
for s in result.value.meetingTimeSuggestions:
24+
slot = s.meeting_time_slot
25+
print(f" {slot.start}{slot.end} (confidence: {s.confidence})")
4326

4427

4528
if __name__ == "__main__":

office365/directory/users/user.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import json
4-
from datetime import datetime
4+
from datetime import datetime, timedelta
55
from typing import Optional
66

77
from typing_extensions import Self
@@ -54,6 +54,7 @@
5454
from office365.outlook.calendar.attendees.base import AttendeeBase
5555
from office365.outlook.calendar.calendar import Calendar
5656
from office365.outlook.calendar.dateTimeTimeZone import DateTimeTimeZone
57+
from office365.outlook.calendar.email_address import EmailAddress
5758
from office365.outlook.calendar.events.event import Event
5859
from office365.outlook.calendar.events.reminder import Reminder
5960
from office365.outlook.calendar.group import CalendarGroup
@@ -85,6 +86,7 @@
8586
from office365.runtime.queries.function import FunctionQuery
8687
from office365.runtime.queries.service_operation import ServiceOperationQuery
8788
from office365.runtime.types.collections import StringCollection
89+
from office365.runtime.types.duration import Duration
8890
from office365.runtime.types.odata_property import odata
8991
from office365.teams.chats.collection import ChatCollection
9092
from office365.teams.collection import TeamCollection
@@ -396,10 +398,10 @@ def export_device_and_app_management_data(self) -> ClientResult[ClientValueColle
396398

397399
def find_meeting_times(
398400
self,
399-
attendees: list[AttendeeBase] | None = None,
401+
attendees: list[str] | None = None,
400402
location_constraint: LocationConstraint | None = None,
401403
time_constraint: TimeConstraint | None = None,
402-
meeting_duration: str | None = None,
404+
meeting_duration: Duration | str | timedelta | None = None,
403405
max_candidates: int | None = None,
404406
is_organizer_optional: bool | None = None,
405407
return_suggestion_reasons: bool | None = None,
@@ -439,11 +441,15 @@ def find_meeting_times(
439441
minimum_attendee_percentage (float): The minimum required confidence for a time slot to be returned
440442
in the response. It is a % value ranging from 0 to 100. Optional.
441443
"""
444+
if isinstance(meeting_duration, timedelta):
445+
meeting_duration = Duration.parse(meeting_duration)
442446
payload = {
443-
"attendees": ClientValueCollection(AttendeeBase, attendees),
447+
"attendees": ClientValueCollection(
448+
AttendeeBase, [AttendeeBase(emailAddress=EmailAddress(a)) for a in attendees or []]
449+
),
444450
"locationConstraint": location_constraint,
445451
"timeConstraint": time_constraint,
446-
"meeting_duration": meeting_duration,
452+
"meetingDuration": meeting_duration,
447453
"maxCandidates": max_candidates,
448454
"isOrganizerOptional": is_organizer_optional,
449455
"returnSuggestionReasons": return_suggestion_reasons,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass
4+
from datetime import timedelta
5+
6+
from office365.runtime.client_value import ClientValue
7+
8+
9+
@dataclass
10+
class Duration(ClientValue):
11+
"""Represents an Edm.Duration value (ISO 8601 duration format, e.g. PT1H)."""
12+
13+
value: str | None = None
14+
15+
@staticmethod
16+
def parse(value: str | timedelta) -> Duration:
17+
if isinstance(value, timedelta):
18+
total_sec = int(value.total_seconds())
19+
h, r = divmod(total_sec, 3600)
20+
m = r // 60
21+
result = "PT"
22+
if h:
23+
result += f"{h}H"
24+
if m:
25+
result += f"{m}M"
26+
return Duration(value=result or "PT0M")
27+
return Duration(value=value)
28+
29+
@property
30+
def entity_type_name(self):
31+
return None

0 commit comments

Comments
 (0)