Skip to content

Commit 59fd59c

Browse files
Improved support to handle large collections, type hints
1 parent 9f2f564 commit 59fd59c

32 files changed

+307
-82
lines changed

examples/sharepoint/read_large_list.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,22 @@
22
from office365.sharepoint.client_context import ClientContext
33
from settings import settings
44

5-
ctx = ClientContext.connect_with_credentials("https://mediadev8.sharepoint.com/sites/team",
6-
ClientCredential(settings['client_credentials']['client_id'],
7-
settings['client_credentials']['client_secret']))
8-
95

10-
def get_all_list_items():
11-
# 1st approach: get all list items
12-
list_source = ctx.web.lists.get_by_title("Contacts_Large")
13-
items = list_source.items
14-
ctx.load(items)
15-
ctx.execute_query()
16-
return items
6+
def print_progress(items_read):
7+
print("Items read: {0}".format(items_read))
178

189

19-
def get_list_items_paged():
20-
list_source = ctx.web.lists.get_by_title("Contacts_Large")
21-
items = list_source.get_items()
22-
ctx.load(items)
23-
ctx.execute_query()
24-
return items
10+
ctx = ClientContext.connect_with_credentials("https://mediadev8.sharepoint.com/sites/team",
11+
ClientCredential(settings['client_credentials']['client_id'],
12+
settings['client_credentials']['client_secret']))
2513

14+
list_source = ctx.web.lists.get_by_title("Contacts_Large")
15+
items = list_source.items
16+
items.page_loaded += print_progress # page load event
17+
items.page_size = 400 # specify custom page size (default is 100)
18+
ctx.load(items)
19+
ctx.execute_query()
2620

27-
all_items = get_list_items_paged()
28-
print("Items count: {0}".format(len(all_items)))
29-
for idx, item in enumerate(all_items):
30-
print("{0}: {1}".format(idx, item.properties['Title']))
21+
#print("Items count: {0}".format(len(items)))
22+
for item in items:
23+
print("{0}".format(item.properties['Title']))

office365/graph/directory/directoryObject.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from office365.graph.entity import Entity
2-
from office365.runtime.client_query import DeleteEntityQuery
2+
from office365.runtime.client_query import DeleteEntityQuery, UpdateEntityQuery
33
from office365.runtime.client_result import ClientResult
44
from office365.runtime.serviceOperationQuery import ServiceOperationQuery
55

@@ -10,7 +10,9 @@ class DirectoryObject(Entity):
1010

1111
def get_member_groups(self, security_enabled_only=True):
1212
"""Return all the groups that the specified user, group, or directory object is a member of. This function is
13-
transitive. """
13+
transitive.
14+
15+
:type security_enabled_only: bool"""
1416
result = ClientResult(None)
1517
payload = {
1618
"securityEnabledOnly": security_enabled_only
@@ -19,6 +21,11 @@ def get_member_groups(self, security_enabled_only=True):
1921
self.context.add_query(qry)
2022
return result
2123

24+
def update(self):
25+
"""Updates the directory object."""
26+
qry = UpdateEntityQuery(self)
27+
self.context.add_query(qry)
28+
2229
def delete_object(self):
2330
"""Deletes the directory object."""
2431
qry = DeleteEntityQuery(self)

office365/graph/directory/group.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Group(DirectoryObject):
1616
def add_team(self):
1717
"""Create a new team under a group."""
1818
team = Team(self.context)
19+
team._parent_collection = self.parent_collection
1920
qry = ServiceOperationQuery(self, "team", None, team, None, team)
2021
self.context.add_query(qry)
2122
self.context.get_pending_request().beforeExecute += self._construct_create_team_request

office365/graph/directory/user.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,29 @@
88
from office365.runtime.serviceOperationQuery import ServiceOperationQuery
99

1010

11+
def _delete_user_from_directory(target_user):
12+
"""
13+
Deletes the user from directory
14+
15+
:type target_user: User
16+
"""
17+
deleted_user = target_user.context.directory.deletedUsers[target_user.id]
18+
deleted_user.delete_object()
19+
20+
1121
class User(DirectoryObject):
1222
"""Represents an Azure AD user account. Inherits from directoryObject."""
1323

24+
def delete_object(self, permanent_delete=False):
25+
"""
26+
:param permanent_delete: Permanently deletes the user from directory
27+
:type permanent_delete: bool
28+
29+
"""
30+
super(User, self).delete_object()
31+
if permanent_delete:
32+
self.ensure_property("id", _delete_user_from_directory)
33+
1434
@property
1535
def drive(self):
1636
"""Retrieve the properties and relationships of a Drive resource."""

office365/graph/directory/userCollection.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ def __getitem__(self, key):
1717
return User(self.context, ResourcePath(key, self.resource_path))
1818

1919
def add(self, user_properties):
20-
"""Create a new user."""
20+
"""Create a new user.
21+
22+
:type user_properties: UserCreationProperties
23+
"""
2124
usr = User(self.context)
2225
qry = CreateEntityQuery(self, user_properties, usr)
2326
self.context.add_query(qry)

office365/graph/directory/userCreationProperties.py

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from office365.graph.directory.passwordProfile import PasswordProfile
2+
from office365.runtime.client_value_object import ClientValueObject
3+
4+
5+
class UserProfile(ClientValueObject):
6+
def __init__(self, principal_name, password, display_name=None, account_enabled=False):
7+
"""
8+
User profile
9+
10+
:type principal_name: str
11+
:type password: str
12+
:type display_name: str
13+
:type account_enabled: bool
14+
"""
15+
super(UserProfile, self).__init__()
16+
self.userPrincipalName = principal_name
17+
self.passwordProfile = PasswordProfile(password)
18+
self.mailNickname = principal_name.split("@")[0]
19+
self.displayName = display_name or principal_name.split("@")[0]
20+
self.accountEnabled = account_enabled

office365/graph/onedrive/drive.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ class Drive(BaseItem):
99
"""The drive resource is the top level object representing a user's OneDrive or a document library in
1010
SharePoint. """
1111

12+
@property
13+
def sharedWithMe(self):
14+
"""Retrieve a collection of DriveItem resources that have been shared with the owner of the Drive."""
15+
if self.is_property_available("sharedWithMe"):
16+
return self.properties['sharedWithMe']
17+
else:
18+
return DriveItemCollection(self.context, ResourcePath("sharedWithMe", self.resource_path))
19+
1220
@property
1321
def root(self):
1422
"""The root folder of the drive."""

office365/graph/onedrive/photo.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from office365.runtime.client_value_object import ClientValueObject
2+
3+
4+
class Photo(ClientValueObject):
5+
"""The photo resource provides photo and camera properties, for example, EXIF metadata, on a driveItem."""
6+
pass

office365/graph/onedrive/video.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from office365.runtime.client_value_object import ClientValueObject
2+
3+
4+
class Video(ClientValueObject):
5+
"""The Video resource groups video-related data items into a single structure."""
6+
pass

office365/graph/teams/channel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from office365.runtime.client_object import ClientObject
1+
from office365.graph.entity import Entity
22

33

4-
class Channel(ClientObject):
4+
class Channel(Entity):
55
"""Teams are made up of channels, which are the conversations you have with your teammates"""
66

77
@property

office365/graph/teams/chatMessage.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from office365.graph.entity import Entity
2+
3+
4+
class ChatMessage(Entity):
5+
pass

office365/graph/teams/team.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from office365.runtime.client_object import ClientObject
1+
from office365.graph.entity import Entity
22
from office365.runtime.client_query import UpdateEntityQuery
33
from office365.runtime.resource_path import ResourcePath
44
from office365.runtime.serviceOperationQuery import ServiceOperationQuery
@@ -10,7 +10,7 @@
1010
from office365.graph.teams.teamMessagingSettings import TeamMessagingSettings
1111

1212

13-
class Team(ClientObject):
13+
class Team(Entity):
1414
"""A team in Microsoft Teams is a collection of channel objects. A channel represents a topic, and therefore a
1515
logical isolation of discussion, within a team. """
1616

@@ -24,12 +24,18 @@ def __init__(self, context, resource_path=None):
2424
@property
2525
def channels(self):
2626
"""The collection of channels & messages associated with the team."""
27-
return ChannelCollection(self, ResourcePath("channels"))
27+
if self.is_property_available("channels"):
28+
return self.properties['channels']
29+
else:
30+
return ChannelCollection(self.context, ResourcePath("channels", self.resource_path))
2831

2932
@property
3033
def primaryChannel(self):
3134
"""The general channel for the team."""
32-
return Channel(self, ResourcePath("primaryChannel"))
35+
if self.is_property_available("primaryChannel"):
36+
return self.properties['primaryChannel']
37+
else:
38+
return Channel(self.context, ResourcePath("primaryChannel"))
3339

3440
def update(self):
3541
"""Updates team."""

office365/graph/teams/teamFunSettings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ class TeamFunSettings(ClientValueObject):
55
"""Settings to configure use of Giphy, memes, and stickers in the team."""
66

77
def __init__(self):
8+
super().__init__()
89
self.allowGiphy = True
910

office365/graph/teams/teamGuestSettings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
class TeamGuestSettings(ClientValueObject):
55

66
def __init__(self):
7-
pass
7+
super().__init__()

office365/graph/teams/teamMemberSettings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class TeamMemberSettings(ClientValueObject):
66
in the team. """
77

88
def __init__(self):
9+
super().__init__()
910
self.allowCreateUpdateChannels = True
1011
self.allowDeleteChannels = True
1112
self.allowAddRemoveApps = True

office365/graph/teams/teamMessagingSettings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
class TeamMessagingSettings(ClientValueObject):
55

66
def __init__(self):
7+
super().__init__()
78
self.allowUserEditMessages = True
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from office365.graph.entity import Entity
2+
3+
4+
class TeamsAsyncOperation(Entity):
5+
pass
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from office365.graph.teams.teamsAsyncOperation import TeamsAsyncOperation
2+
from office365.runtime.client_object_collection import ClientObjectCollection
3+
4+
5+
class TeamsAsyncOperationCollection(ClientObjectCollection):
6+
"""TeamsAsyncOperation's collection"""
7+
8+
def __init__(self, context, resource_path=None):
9+
super(TeamsAsyncOperationCollection, self).__init__(context, TeamsAsyncOperation, resource_path)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class TeamsAsyncOperationStatus:
2+
3+
def __init__(self):
4+
pass

office365/runtime/client_object.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ class ClientObject(object):
66
"""Base client object"""
77

88
def __init__(self, context, resource_path=None, properties=None, parent_collection=None):
9+
"""
10+
11+
:type parent_collection: office365.runtime.client_object_collection.ClientObjectCollection
12+
:type properties: dict
13+
:type resource_path: office365.runtime.resource_path.ResourcePath
14+
:type context: office365.runtime.client_runtime_context.ClientRuntimeContext
15+
"""
916
self._properties = {}
1017
self._changes = []
1118
self._entity_type_name = None
@@ -55,12 +62,18 @@ def set_property(self, name, value, persist_changes=True):
5562
def to_json(self):
5663
return dict((k, v) for k, v in self.properties.items() if k in self._changes)
5764

58-
def ensure_property(self, name, loaded):
65+
def ensure_property(self, name, action):
66+
"""
67+
Ensures property is loaded
68+
69+
:type action: any
70+
:type name: str
71+
"""
5972
if not self.is_property_available(name):
6073
self.context.load(self, [name])
61-
self.context.afterExecuteOnce += loaded
74+
self.context.afterExecuteOnce += action
6275
else:
63-
loaded(self)
76+
action(self)
6477

6578
@property
6679
def entity_type_name(self):

0 commit comments

Comments
 (0)