smartsheet-python-sdk 3.5.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smartsheet/__init__.py +37 -0
- smartsheet/attachments.py +565 -0
- smartsheet/cells.py +164 -0
- smartsheet/contacts.py +78 -0
- smartsheet/discussions.py +411 -0
- smartsheet/events.py +79 -0
- smartsheet/exceptions.py +130 -0
- smartsheet/favorites.py +116 -0
- smartsheet/folders.py +438 -0
- smartsheet/groups.py +186 -0
- smartsheet/home.py +180 -0
- smartsheet/images.py +61 -0
- smartsheet/models/__init__.py +126 -0
- smartsheet/models/access_token.py +95 -0
- smartsheet/models/account.py +77 -0
- smartsheet/models/alternate_email.py +88 -0
- smartsheet/models/asset_share.py +165 -0
- smartsheet/models/asset_shares_paginated_result.py +84 -0
- smartsheet/models/attachment.py +181 -0
- smartsheet/models/auto_number_format.py +81 -0
- smartsheet/models/automation_action.py +162 -0
- smartsheet/models/automation_rule.py +164 -0
- smartsheet/models/boolean_object_value.py +38 -0
- smartsheet/models/bulk_item_failure.py +77 -0
- smartsheet/models/bulk_item_result.py +111 -0
- smartsheet/models/cell.py +193 -0
- smartsheet/models/cell_data_item.py +152 -0
- smartsheet/models/cell_history.py +67 -0
- smartsheet/models/cell_link.py +91 -0
- smartsheet/models/cell_link_widget_content.py +101 -0
- smartsheet/models/chart_widget_content.py +124 -0
- smartsheet/models/column.py +253 -0
- smartsheet/models/comment.py +126 -0
- smartsheet/models/contact.py +88 -0
- smartsheet/models/contact_object_value.py +59 -0
- smartsheet/models/container_destination.py +74 -0
- smartsheet/models/copy_or_move_row_destination.py +54 -0
- smartsheet/models/copy_or_move_row_directive.py +64 -0
- smartsheet/models/copy_or_move_row_result.py +67 -0
- smartsheet/models/criteria.py +82 -0
- smartsheet/models/cross_sheet_reference.py +134 -0
- smartsheet/models/currency.py +64 -0
- smartsheet/models/date_object_value.py +56 -0
- smartsheet/models/datetime_object_value.py +56 -0
- smartsheet/models/discussion.py +183 -0
- smartsheet/models/downloaded_file.py +106 -0
- smartsheet/models/duration.py +112 -0
- smartsheet/models/email.py +82 -0
- smartsheet/models/enums/__init__.py +56 -0
- smartsheet/models/enums/access_level.py +26 -0
- smartsheet/models/enums/asset_type.py +10 -0
- smartsheet/models/enums/attachment_parent_type.py +23 -0
- smartsheet/models/enums/attachment_sub_type.py +26 -0
- smartsheet/models/enums/attachment_type.py +29 -0
- smartsheet/models/enums/automation_action_frequency.py +24 -0
- smartsheet/models/enums/automation_action_type.py +23 -0
- smartsheet/models/enums/automation_rule_disabled_reason.py +27 -0
- smartsheet/models/enums/cell_link_status.py +28 -0
- smartsheet/models/enums/column_type.py +31 -0
- smartsheet/models/enums/criteria_target.py +21 -0
- smartsheet/models/enums/cross_sheet_reference_status.py +28 -0
- smartsheet/models/enums/currency_code.py +43 -0
- smartsheet/models/enums/day_descriptors.py +30 -0
- smartsheet/models/enums/day_ordinal.py +25 -0
- smartsheet/models/enums/event_action.py +76 -0
- smartsheet/models/enums/event_obejct_type.py +34 -0
- smartsheet/models/enums/event_source.py +27 -0
- smartsheet/models/enums/global_template.py +23 -0
- smartsheet/models/enums/operator.py +62 -0
- smartsheet/models/enums/paper_type.py +29 -0
- smartsheet/models/enums/predecessor_type.py +24 -0
- smartsheet/models/enums/publish_accessible_by.py +22 -0
- smartsheet/models/enums/schedule_type.py +25 -0
- smartsheet/models/enums/seat_type.py +17 -0
- smartsheet/models/enums/share_scope.py +22 -0
- smartsheet/models/enums/share_type.py +22 -0
- smartsheet/models/enums/sheet_email_format.py +23 -0
- smartsheet/models/enums/sheet_filter_operator.py +22 -0
- smartsheet/models/enums/sheet_filter_type.py +23 -0
- smartsheet/models/enums/sort_direction.py +22 -0
- smartsheet/models/enums/symbol.py +45 -0
- smartsheet/models/enums/system_column_type.py +25 -0
- smartsheet/models/enums/update_request_status.py +23 -0
- smartsheet/models/enums/user_status.py +23 -0
- smartsheet/models/enums/widget_type.py +32 -0
- smartsheet/models/error.py +74 -0
- smartsheet/models/error_result.py +117 -0
- smartsheet/models/event.py +153 -0
- smartsheet/models/event_result.py +86 -0
- smartsheet/models/explicit_null.py +24 -0
- smartsheet/models/favorite.py +81 -0
- smartsheet/models/folder.py +177 -0
- smartsheet/models/font_family.py +63 -0
- smartsheet/models/format_details.py +55 -0
- smartsheet/models/format_tables.py +191 -0
- smartsheet/models/group.py +134 -0
- smartsheet/models/group_member.py +104 -0
- smartsheet/models/home.py +110 -0
- smartsheet/models/hyperlink.py +81 -0
- smartsheet/models/image.py +101 -0
- smartsheet/models/image_url.py +91 -0
- smartsheet/models/image_url_map.py +68 -0
- smartsheet/models/image_widget_content.py +117 -0
- smartsheet/models/index_result.py +118 -0
- smartsheet/models/json_object.py +59 -0
- smartsheet/models/multi_contact_object_value.py +49 -0
- smartsheet/models/multi_picklist_object_value.py +48 -0
- smartsheet/models/multi_row_email.py +60 -0
- smartsheet/models/number_object_value.py +38 -0
- smartsheet/models/o_auth_error.py +86 -0
- smartsheet/models/object_value.py +130 -0
- smartsheet/models/paginated_children_result.py +80 -0
- smartsheet/models/predecessor.py +102 -0
- smartsheet/models/predecessor_list.py +49 -0
- smartsheet/models/primitive_object_value.py +59 -0
- smartsheet/models/profile_image.py +72 -0
- smartsheet/models/project_settings.py +89 -0
- smartsheet/models/recipient.py +63 -0
- smartsheet/models/report.py +90 -0
- smartsheet/models/report_cell.py +59 -0
- smartsheet/models/report_column.py +67 -0
- smartsheet/models/report_publish.py +95 -0
- smartsheet/models/report_row.py +68 -0
- smartsheet/models/report_widget_content.py +78 -0
- smartsheet/models/result.py +105 -0
- smartsheet/models/row.py +336 -0
- smartsheet/models/row_email.py +83 -0
- smartsheet/models/row_mapping.py +77 -0
- smartsheet/models/schedule.py +140 -0
- smartsheet/models/scope.py +70 -0
- smartsheet/models/search_result.py +67 -0
- smartsheet/models/search_result_item.py +150 -0
- smartsheet/models/selection_range.py +86 -0
- smartsheet/models/sent_update_request.py +172 -0
- smartsheet/models/server_info.py +67 -0
- smartsheet/models/share.py +183 -0
- smartsheet/models/sheet.py +462 -0
- smartsheet/models/sheet_email.py +81 -0
- smartsheet/models/sheet_filter.py +106 -0
- smartsheet/models/sheet_filter_details.py +76 -0
- smartsheet/models/sheet_publish.py +184 -0
- smartsheet/models/sheet_summary.py +59 -0
- smartsheet/models/sheet_user_permissions.py +58 -0
- smartsheet/models/sheet_user_settings.py +72 -0
- smartsheet/models/shortcut_data_item.py +102 -0
- smartsheet/models/shortcut_widget_content.py +61 -0
- smartsheet/models/sight.py +175 -0
- smartsheet/models/sight_publish.py +77 -0
- smartsheet/models/sort_criterion.py +64 -0
- smartsheet/models/sort_specifier.py +55 -0
- smartsheet/models/source.py +83 -0
- smartsheet/models/string_object_value.py +38 -0
- smartsheet/models/summary_field.py +256 -0
- smartsheet/models/template.py +171 -0
- smartsheet/models/title_rich_text_widget_content.py +68 -0
- smartsheet/models/token_paginated_result.py +79 -0
- smartsheet/models/update_request.py +110 -0
- smartsheet/models/user.py +58 -0
- smartsheet/models/user_model.py +280 -0
- smartsheet/models/user_plan.py +77 -0
- smartsheet/models/user_profile.py +89 -0
- smartsheet/models/version.py +57 -0
- smartsheet/models/web_content_widget_content.py +60 -0
- smartsheet/models/webhook.py +219 -0
- smartsheet/models/webhook_secret.py +58 -0
- smartsheet/models/webhook_stats.py +76 -0
- smartsheet/models/webhook_subscope.py +50 -0
- smartsheet/models/widget.py +211 -0
- smartsheet/models/widget_content.py +52 -0
- smartsheet/models/widget_hyperlink.py +74 -0
- smartsheet/models/workspace.py +185 -0
- smartsheet/object_value.py +72 -0
- smartsheet/passthrough.py +127 -0
- smartsheet/reports.py +382 -0
- smartsheet/search.py +100 -0
- smartsheet/server.py +48 -0
- smartsheet/session.py +70 -0
- smartsheet/sharing.py +163 -0
- smartsheet/sheets.py +2062 -0
- smartsheet/sights.py +370 -0
- smartsheet/smartsheet.py +684 -0
- smartsheet/templates.py +87 -0
- smartsheet/token.py +128 -0
- smartsheet/types.py +323 -0
- smartsheet/users.py +490 -0
- smartsheet/util.py +199 -0
- smartsheet/version.py +34 -0
- smartsheet/webhooks.py +161 -0
- smartsheet/workspaces.py +647 -0
- smartsheet_python_sdk-3.5.5.dist-info/METADATA +120 -0
- smartsheet_python_sdk-3.5.5.dist-info/RECORD +195 -0
- smartsheet_python_sdk-3.5.5.dist-info/WHEEL +5 -0
- smartsheet_python_sdk-3.5.5.dist-info/licenses/LICENSE.md +201 -0
- smartsheet_python_sdk-3.5.5.dist-info/licenses/NOTICE +10 -0
- smartsheet_python_sdk-3.5.5.dist-info/top_level.txt +1 -0
smartsheet/users.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# pylint: disable=C0111,R0902,R0913
|
|
2
|
+
# Smartsheet Python SDK.
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2018 Smartsheet.com, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
7
|
+
# not use this file except in compliance with the License. You may obtain
|
|
8
|
+
# a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
14
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
15
|
+
# License for the specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
from __future__ import absolute_import
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
|
|
23
|
+
from . import fresh_operation
|
|
24
|
+
from .models.enums.seat_type import SeatType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Users:
|
|
28
|
+
|
|
29
|
+
"""Class for handling Users operations."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, smartsheet_obj):
|
|
32
|
+
"""Init Users with base Smartsheet object."""
|
|
33
|
+
self._base = smartsheet_obj
|
|
34
|
+
self._log = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
def add_alternate_email(self, user_id, list_of_alternate_emails):
|
|
37
|
+
"""Add one or more alternate email addresses for the specified User
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
user_id (int): User ID
|
|
41
|
+
list_of_alternate_emails (list[AlternateEmail]):
|
|
42
|
+
An array of one or more AlternateEmail objects.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Result
|
|
46
|
+
"""
|
|
47
|
+
_op = fresh_operation("add_alternate_email")
|
|
48
|
+
_op["method"] = "POST"
|
|
49
|
+
_op["path"] = "/users/" + str(user_id) + "/alternateemails"
|
|
50
|
+
_op["json"] = list_of_alternate_emails
|
|
51
|
+
|
|
52
|
+
expected = ["Result", "AlternateEmail"]
|
|
53
|
+
|
|
54
|
+
prepped_request = self._base.prepare_request(_op)
|
|
55
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
56
|
+
|
|
57
|
+
return response
|
|
58
|
+
|
|
59
|
+
def promote_alternate_email(self, user_id, alt_id):
|
|
60
|
+
"""Promote an email address to primary
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
user_id (int): User ID
|
|
64
|
+
alt_id(int): AlternateEmail ID to be promoted
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Result
|
|
68
|
+
"""
|
|
69
|
+
_op = fresh_operation("promote_alternate_email")
|
|
70
|
+
_op["method"] = "POST"
|
|
71
|
+
_op["path"] = (
|
|
72
|
+
"/users/"
|
|
73
|
+
+ str(user_id)
|
|
74
|
+
+ "/alternateemails/"
|
|
75
|
+
+ str(alt_id)
|
|
76
|
+
+ "/makeprimary"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
expected = ["Result", "AlternateEmail"]
|
|
80
|
+
|
|
81
|
+
prepped_request = self._base.prepare_request(_op)
|
|
82
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
83
|
+
|
|
84
|
+
return response
|
|
85
|
+
|
|
86
|
+
def add_user(self, user_obj, send_email=None):
|
|
87
|
+
"""Add a User to the organization.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
user_obj (User): User object with the following attributes:
|
|
91
|
+
|
|
92
|
+
email (required)
|
|
93
|
+
|
|
94
|
+
admin (required)
|
|
95
|
+
|
|
96
|
+
licensedSheetCreator (required)
|
|
97
|
+
|
|
98
|
+
firstName (optional)
|
|
99
|
+
|
|
100
|
+
lastName (optional)
|
|
101
|
+
|
|
102
|
+
resourceViewer (optional)
|
|
103
|
+
|
|
104
|
+
send_email (bool): Either true or false to indicate
|
|
105
|
+
whether or not to notify the user by email. Default is false.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Result
|
|
109
|
+
"""
|
|
110
|
+
_op = fresh_operation("add_user")
|
|
111
|
+
_op["method"] = "POST"
|
|
112
|
+
_op["path"] = "/users"
|
|
113
|
+
_op["json"] = user_obj
|
|
114
|
+
_op["query_params"]["sendEmail"] = send_email
|
|
115
|
+
|
|
116
|
+
expected = ["Result", "User"]
|
|
117
|
+
|
|
118
|
+
prepped_request = self._base.prepare_request(_op)
|
|
119
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
120
|
+
|
|
121
|
+
return response
|
|
122
|
+
|
|
123
|
+
def delete_alternate_email(self, user_id, alternate_email_id):
|
|
124
|
+
"""Deletes the specified alternate email address for the specified User.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
user_id (int): User ID
|
|
128
|
+
alternate_email_id (int): Alternate Email ID
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Result
|
|
132
|
+
"""
|
|
133
|
+
_op = fresh_operation("delete_alternate_email")
|
|
134
|
+
_op["method"] = "DELETE"
|
|
135
|
+
_op["path"] = (
|
|
136
|
+
"/users/" + str(user_id) + "/alternateemails/" + str(alternate_email_id)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
expected = ["Result", None]
|
|
140
|
+
prepped_request = self._base.prepare_request(_op)
|
|
141
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
142
|
+
|
|
143
|
+
return response
|
|
144
|
+
|
|
145
|
+
def get_alternate_email(self, user_id, alternate_email_id):
|
|
146
|
+
"""Get the specified Alternate Email
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
user_id (int): User ID
|
|
150
|
+
alternate_email_id (int): Alternate Email ID
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
AlternateEmail
|
|
154
|
+
"""
|
|
155
|
+
_op = fresh_operation("get_alternate_email")
|
|
156
|
+
_op["method"] = "GET"
|
|
157
|
+
_op["path"] = (
|
|
158
|
+
"/users/" + str(user_id) + "/alternateemails/" + str(alternate_email_id)
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
expected = "AlternateEmail"
|
|
162
|
+
prepped_request = self._base.prepare_request(_op)
|
|
163
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
164
|
+
|
|
165
|
+
return response
|
|
166
|
+
|
|
167
|
+
def get_current_user(self, include=None):
|
|
168
|
+
"""Get the currently authenticated User.
|
|
169
|
+
Returns:
|
|
170
|
+
UserProfile
|
|
171
|
+
"""
|
|
172
|
+
_op = fresh_operation("get_current_user")
|
|
173
|
+
_op["method"] = "GET"
|
|
174
|
+
_op["path"] = "/users/me"
|
|
175
|
+
_op["query_params"]["include"] = include
|
|
176
|
+
|
|
177
|
+
expected = "UserProfile"
|
|
178
|
+
prepped_request = self._base.prepare_request(_op)
|
|
179
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
180
|
+
|
|
181
|
+
return response
|
|
182
|
+
|
|
183
|
+
def get_user(self, user_id):
|
|
184
|
+
"""Get the specified User.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
user_id (int): User ID
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
UserProfile
|
|
191
|
+
"""
|
|
192
|
+
_op = fresh_operation("get_user")
|
|
193
|
+
_op["method"] = "GET"
|
|
194
|
+
_op["path"] = "/users/" + str(user_id)
|
|
195
|
+
|
|
196
|
+
expected = "UserProfile"
|
|
197
|
+
prepped_request = self._base.prepare_request(_op)
|
|
198
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
199
|
+
|
|
200
|
+
return response
|
|
201
|
+
|
|
202
|
+
def list_alternate_emails(self, user_id):
|
|
203
|
+
"""Get a list of the Alternate Emails for the specified User.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
user_id (int): User ID
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
IndexResult
|
|
210
|
+
"""
|
|
211
|
+
_op = fresh_operation("list_alternate_emails")
|
|
212
|
+
_op["method"] = "GET"
|
|
213
|
+
_op["path"] = "/users/" + str(user_id) + "/alternateemails"
|
|
214
|
+
|
|
215
|
+
expected = ["IndexResult", "AlternateEmail"]
|
|
216
|
+
|
|
217
|
+
prepped_request = self._base.prepare_request(_op)
|
|
218
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
219
|
+
|
|
220
|
+
return response
|
|
221
|
+
|
|
222
|
+
def list_org_sheets(
|
|
223
|
+
self, page_size=None, page=None, include_all=None, modified_since=None
|
|
224
|
+
):
|
|
225
|
+
"""Get a list of all Sheets owned by an organization.
|
|
226
|
+
|
|
227
|
+
Get the list of all Sheets owned by the members of the
|
|
228
|
+
account (organization).
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
page_size (int): The maximum number of items to
|
|
232
|
+
return per page.
|
|
233
|
+
page (int): Which page to return.
|
|
234
|
+
include_all (bool): If true, include all results
|
|
235
|
+
(i.e. do not paginate).
|
|
236
|
+
modified_since(datetime): list organization sheets modified since datetime
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
IndexResult
|
|
240
|
+
"""
|
|
241
|
+
_op = fresh_operation("list_org_sheets")
|
|
242
|
+
_op["method"] = "GET"
|
|
243
|
+
_op["path"] = "/users/sheets"
|
|
244
|
+
_op["query_params"]["pageSize"] = page_size
|
|
245
|
+
_op["query_params"]["page"] = page
|
|
246
|
+
_op["query_params"]["includeAll"] = include_all
|
|
247
|
+
if isinstance(modified_since, datetime):
|
|
248
|
+
_op["query_params"]["modifiedSince"] = modified_since.isoformat()
|
|
249
|
+
|
|
250
|
+
expected = ["IndexResult", "Sheet"]
|
|
251
|
+
|
|
252
|
+
prepped_request = self._base.prepare_request(_op)
|
|
253
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
254
|
+
|
|
255
|
+
return response
|
|
256
|
+
|
|
257
|
+
def list_users(
|
|
258
|
+
self, email=None, page_size=None, page=None, include_all=None, include=None,
|
|
259
|
+
plan_id=None, seat_type=None
|
|
260
|
+
):
|
|
261
|
+
"""Get the list of Users in the organization.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
email (list[str]): Comma separated list of email
|
|
265
|
+
addresses on which to filter the results.
|
|
266
|
+
page_size (int): The maximum number of items to
|
|
267
|
+
return per page.
|
|
268
|
+
page (int): Which page to return.
|
|
269
|
+
include_all (bool): If true, include all results
|
|
270
|
+
(i.e. do not paginate).
|
|
271
|
+
include(list[str]): optional include parameter, only current
|
|
272
|
+
accepted value is 'lastLogin'
|
|
273
|
+
plan_id(int): optional plan_id parameter, returns users
|
|
274
|
+
in the selected plan.
|
|
275
|
+
seat_type(SeatType): optional seat_type parameter, filters users
|
|
276
|
+
by their seat type.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
IndexResult
|
|
280
|
+
"""
|
|
281
|
+
_op = fresh_operation("list_users")
|
|
282
|
+
_op["method"] = "GET"
|
|
283
|
+
_op["path"] = "/users"
|
|
284
|
+
_op["query_params"]["email"] = email
|
|
285
|
+
_op["query_params"]["include"] = include
|
|
286
|
+
_op["query_params"]["pageSize"] = page_size
|
|
287
|
+
_op["query_params"]["page"] = page
|
|
288
|
+
_op["query_params"]["includeAll"] = include_all
|
|
289
|
+
_op["query_params"]["planId"] = plan_id
|
|
290
|
+
_op["query_params"]["seatType"] = seat_type
|
|
291
|
+
|
|
292
|
+
expected = ["IndexResult", "User"]
|
|
293
|
+
|
|
294
|
+
prepped_request = self._base.prepare_request(_op)
|
|
295
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
296
|
+
|
|
297
|
+
return response
|
|
298
|
+
|
|
299
|
+
def remove_user(
|
|
300
|
+
self,
|
|
301
|
+
user_id,
|
|
302
|
+
transfer_to=None,
|
|
303
|
+
transfer_sheets=False,
|
|
304
|
+
remove_from_sharing=False,
|
|
305
|
+
):
|
|
306
|
+
"""Remove a user from an organization.
|
|
307
|
+
|
|
308
|
+
Remove a User from an organization. User is transitioned to
|
|
309
|
+
a free collaborator with read-only access to owned sheets, unless
|
|
310
|
+
those are optionally transferred to another User.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
user_id (int): User ID
|
|
314
|
+
transfer_to (int): The ID of the User to
|
|
315
|
+
transfer ownership to. If the User being removed owns
|
|
316
|
+
groups, this value is required. Any groups owned by the User
|
|
317
|
+
being removed will be transferred to the specified User. If
|
|
318
|
+
the User owns sheets, _and_ **transferSheets** is `true`,
|
|
319
|
+
the removed User's sheets will be transferred to the
|
|
320
|
+
specified User.
|
|
321
|
+
transfer_sheets (bool): If `true` and
|
|
322
|
+
**transferTo** is specified, the removed User's sheets will
|
|
323
|
+
be transferred. Otherwise, sheets will not be transferred.
|
|
324
|
+
Defaults to `false`.
|
|
325
|
+
remove_from_sharing (bool): Set to `true` to
|
|
326
|
+
remove the user from sharing for all sheets/workspaces in
|
|
327
|
+
the organization. If not specified, User will not be removed
|
|
328
|
+
from sharing.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Result
|
|
332
|
+
"""
|
|
333
|
+
_op = fresh_operation("remove_user")
|
|
334
|
+
_op["method"] = "DELETE"
|
|
335
|
+
_op["path"] = "/users/" + str(user_id)
|
|
336
|
+
_op["query_params"]["transferTo"] = transfer_to
|
|
337
|
+
_op["query_params"]["transferSheets"] = transfer_sheets
|
|
338
|
+
_op["query_params"]["removeFromSharing"] = remove_from_sharing
|
|
339
|
+
|
|
340
|
+
expected = ["Result", None]
|
|
341
|
+
prepped_request = self._base.prepare_request(_op)
|
|
342
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
343
|
+
|
|
344
|
+
return response
|
|
345
|
+
|
|
346
|
+
def update_user(self, user_id, user_obj):
|
|
347
|
+
"""Update the specified User.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
user_id (int): User ID
|
|
351
|
+
user_obj (User): User object with the following
|
|
352
|
+
attributes:
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Result
|
|
356
|
+
"""
|
|
357
|
+
_op = fresh_operation("update_user")
|
|
358
|
+
_op["method"] = "PUT"
|
|
359
|
+
_op["path"] = "/users/" + str(user_id)
|
|
360
|
+
_op["json"] = user_obj
|
|
361
|
+
|
|
362
|
+
expected = ["Result", "User"]
|
|
363
|
+
|
|
364
|
+
prepped_request = self._base.prepare_request(_op)
|
|
365
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
366
|
+
|
|
367
|
+
return response
|
|
368
|
+
|
|
369
|
+
def upgrade_user(self, user_id, plan_id, seat_type):
|
|
370
|
+
"""Upgrades a user for a plan.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
user_id (int): User ID
|
|
374
|
+
plan_id (int): Plan ID
|
|
375
|
+
seat_type (UpgradeSeatType): Seat type to upgrade to
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
dict: Result
|
|
379
|
+
"""
|
|
380
|
+
_op = fresh_operation("upgrade_user")
|
|
381
|
+
_op["method"] = "POST"
|
|
382
|
+
_op["path"] = f"/users/{user_id}/plans/{plan_id}/upgrade"
|
|
383
|
+
_op["json"] = {"seatType": seat_type}
|
|
384
|
+
|
|
385
|
+
expected = ["Result", None]
|
|
386
|
+
|
|
387
|
+
prepped_request = self._base.prepare_request(_op)
|
|
388
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
389
|
+
|
|
390
|
+
return response
|
|
391
|
+
|
|
392
|
+
def downgrade_user(self, user_id, plan_id, seat_type):
|
|
393
|
+
"""Downgrades a user for a plan.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
user_id (int): User ID
|
|
397
|
+
plan_id (int): Plan ID
|
|
398
|
+
seat_type (DowngradeSeatType): Seat type to downgrade to
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
dict: Result
|
|
402
|
+
"""
|
|
403
|
+
_op = fresh_operation("downgrade_user")
|
|
404
|
+
_op["method"] = "POST"
|
|
405
|
+
_op["path"] = f"/users/{user_id}/plans/{plan_id}/downgrade"
|
|
406
|
+
_op["json"] = {"seatType": seat_type}
|
|
407
|
+
|
|
408
|
+
expected = ["Result", None]
|
|
409
|
+
|
|
410
|
+
prepped_request = self._base.prepare_request(_op)
|
|
411
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
412
|
+
|
|
413
|
+
return response
|
|
414
|
+
|
|
415
|
+
def list_user_plans(self, user_id, last_key=None, max_items=None):
|
|
416
|
+
"""List user's plans.
|
|
417
|
+
Args:
|
|
418
|
+
user_id (int): User ID
|
|
419
|
+
Returns:
|
|
420
|
+
TokenPaginatedResult
|
|
421
|
+
"""
|
|
422
|
+
_op = fresh_operation("list_user_plans")
|
|
423
|
+
_op["method"] = "GET"
|
|
424
|
+
_op["path"] = f"/users/{user_id}/plans"
|
|
425
|
+
_op["query_params"]["lastKey"] = last_key
|
|
426
|
+
_op["query_params"]["maxItems"] = max_items
|
|
427
|
+
|
|
428
|
+
expected = ["TokenPaginatedResult", "UserPlan"]
|
|
429
|
+
|
|
430
|
+
prepped_request = self._base.prepare_request(_op)
|
|
431
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
432
|
+
|
|
433
|
+
return response
|
|
434
|
+
|
|
435
|
+
def remove_user_from_plan(self, user_id, plan_id):
|
|
436
|
+
"""Remove user from plan.
|
|
437
|
+
Args:
|
|
438
|
+
user_id (int): User ID
|
|
439
|
+
plan_id (int): Plan ID
|
|
440
|
+
Returns:
|
|
441
|
+
Result
|
|
442
|
+
"""
|
|
443
|
+
_op = fresh_operation("remove_user_from_plan")
|
|
444
|
+
_op["method"] = "DELETE"
|
|
445
|
+
_op["path"] = f"/users/{user_id}/plans/{plan_id}"
|
|
446
|
+
|
|
447
|
+
expected = ["Result", None]
|
|
448
|
+
|
|
449
|
+
prepped_request = self._base.prepare_request(_op)
|
|
450
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
451
|
+
|
|
452
|
+
return response
|
|
453
|
+
|
|
454
|
+
def add_profile_image(self, user_id, file, file_type):
|
|
455
|
+
"""Uploads a profile image for the specified user.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
user_id (int): user ID
|
|
459
|
+
file (string): path to image file.
|
|
460
|
+
file_type (string): content type of image file
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Result
|
|
464
|
+
"""
|
|
465
|
+
if not all(val is not None for val in ["user_id", "file", "file_type"]):
|
|
466
|
+
raise ValueError(
|
|
467
|
+
("One or more required values are missing from call to " + __name__)
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
return self._attach_profile_image(user_id, file, file_type)
|
|
471
|
+
|
|
472
|
+
def _attach_profile_image(self, user_id, file, file_type):
|
|
473
|
+
"""Internal function used to load image"""
|
|
474
|
+
|
|
475
|
+
_data = open(file, "rb").read()
|
|
476
|
+
_op = fresh_operation("attach_profile_image")
|
|
477
|
+
_op["method"] = "POST"
|
|
478
|
+
_op["path"] = "/users/" + str(user_id) + "/profileimage"
|
|
479
|
+
_op["headers"] = {
|
|
480
|
+
"content-type": file_type,
|
|
481
|
+
"content-disposition": 'attachment; filename="' + file + '"',
|
|
482
|
+
}
|
|
483
|
+
_op["form_data"] = _data
|
|
484
|
+
|
|
485
|
+
expected = ["Result", "User"]
|
|
486
|
+
|
|
487
|
+
prepped_request = self._base.prepare_request(_op)
|
|
488
|
+
response = self._base.request(prepped_request, expected, _op)
|
|
489
|
+
|
|
490
|
+
return response
|
smartsheet/util.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# pylint: disable=R0912
|
|
2
|
+
# Smartsheet Python SDK.
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2018 Smartsheet.com, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
7
|
+
# not use this file except in compliance with the License. You may obtain
|
|
8
|
+
# a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
14
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
15
|
+
# License for the specific language governing permissions and limitations
|
|
16
|
+
# under the License.
|
|
17
|
+
|
|
18
|
+
from __future__ import absolute_import
|
|
19
|
+
|
|
20
|
+
import functools
|
|
21
|
+
import inspect
|
|
22
|
+
import logging
|
|
23
|
+
import re
|
|
24
|
+
import warnings
|
|
25
|
+
from datetime import date, datetime
|
|
26
|
+
|
|
27
|
+
import six
|
|
28
|
+
|
|
29
|
+
from .types import EnumeratedValue, TypedList
|
|
30
|
+
|
|
31
|
+
_log = logging.getLogger(__name__)
|
|
32
|
+
_primitive_types = (six.string_types, six.integer_types, float, bool)
|
|
33
|
+
_list_types = (TypedList, list)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _camel_to_underscore(name):
|
|
37
|
+
camel_pat = re.compile(r"([A-Z])")
|
|
38
|
+
return camel_pat.sub(lambda x: "_" + x.group(1).lower(), name)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _underscore_to_camel(name):
|
|
42
|
+
under_pat = re.compile(r"_([a-z])")
|
|
43
|
+
return under_pat.sub(lambda x: x.group(1).upper(), name)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def prep(prop, op_id=None, method=None):
|
|
47
|
+
"""Serialize a value for JSON transformation."""
|
|
48
|
+
if isinstance(prop, (datetime, date)):
|
|
49
|
+
retval = prop.isoformat()
|
|
50
|
+
|
|
51
|
+
elif hasattr(prop, "to_list"):
|
|
52
|
+
# Export TypedList recursively
|
|
53
|
+
proplist = prop.to_list()
|
|
54
|
+
retval = [prep(x) for x in proplist]
|
|
55
|
+
|
|
56
|
+
elif hasattr(prop, "to_dict"):
|
|
57
|
+
retval = prop.to_dict(op_id, method)
|
|
58
|
+
|
|
59
|
+
elif isinstance(prop, list):
|
|
60
|
+
retval = [
|
|
61
|
+
(x.to_dict(op_id, method) if hasattr(x, "to_dict") else x) for x in prop
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
retval = prop
|
|
66
|
+
|
|
67
|
+
return retval
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_child_properties(obj):
|
|
71
|
+
retval = []
|
|
72
|
+
prop_list = inspect.getmembers(obj.__class__, inspect.isdatadescriptor)
|
|
73
|
+
for prop in prop_list:
|
|
74
|
+
if isinstance(prop[1], property):
|
|
75
|
+
prop_name = prop[0]
|
|
76
|
+
camel_case = _underscore_to_camel(prop_name)
|
|
77
|
+
camel_case = camel_case.rstrip(
|
|
78
|
+
"_"
|
|
79
|
+
) # trim trailing '_' from props with names eq. to built-ins
|
|
80
|
+
retval.append((prop_name, camel_case))
|
|
81
|
+
|
|
82
|
+
return retval
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def serialize(obj):
|
|
86
|
+
|
|
87
|
+
retval = None
|
|
88
|
+
|
|
89
|
+
if hasattr(obj, "serialize"):
|
|
90
|
+
retval = obj.serialize()
|
|
91
|
+
|
|
92
|
+
elif isinstance(obj, datetime):
|
|
93
|
+
retval = obj.isoformat() + "Z"
|
|
94
|
+
|
|
95
|
+
elif isinstance(obj, date):
|
|
96
|
+
retval = obj.isoformat()
|
|
97
|
+
|
|
98
|
+
elif isinstance(obj, _primitive_types):
|
|
99
|
+
retval = obj
|
|
100
|
+
|
|
101
|
+
elif hasattr(obj, "is_explicit_null"):
|
|
102
|
+
retval = obj
|
|
103
|
+
|
|
104
|
+
elif isinstance(obj, EnumeratedValue):
|
|
105
|
+
if obj.value is not None:
|
|
106
|
+
retval = obj.value.name
|
|
107
|
+
|
|
108
|
+
elif isinstance(obj, _list_types):
|
|
109
|
+
if len(obj):
|
|
110
|
+
retval = []
|
|
111
|
+
for item in obj:
|
|
112
|
+
serialized = serialize(item)
|
|
113
|
+
if not hasattr(serialized, "is_explicit_null"):
|
|
114
|
+
retval.append(serialized)
|
|
115
|
+
|
|
116
|
+
elif isinstance(obj, dict):
|
|
117
|
+
retval = {}
|
|
118
|
+
for key, value in obj.items():
|
|
119
|
+
if value is None:
|
|
120
|
+
retval[key] = None
|
|
121
|
+
else:
|
|
122
|
+
serialized_value = serialize(value)
|
|
123
|
+
if not hasattr(serialized_value, "is_explicit_null"):
|
|
124
|
+
retval[key] = serialized_value
|
|
125
|
+
|
|
126
|
+
else:
|
|
127
|
+
retval = {}
|
|
128
|
+
prop_list = get_child_properties(obj)
|
|
129
|
+
for prop in prop_list:
|
|
130
|
+
prop_name = prop[0]
|
|
131
|
+
camel_case = prop[1]
|
|
132
|
+
prop_value = getattr(obj, prop_name)
|
|
133
|
+
if prop_value is not None:
|
|
134
|
+
serialized = serialize(prop_value)
|
|
135
|
+
if hasattr(
|
|
136
|
+
serialized, "is_explicit_null"
|
|
137
|
+
): # object forcing serialization of a null
|
|
138
|
+
retval[camel_case] = None
|
|
139
|
+
elif serialized is not None:
|
|
140
|
+
retval[camel_case] = serialized
|
|
141
|
+
|
|
142
|
+
return retval
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def deserialize(obj, props):
|
|
146
|
+
if isinstance(props, dict):
|
|
147
|
+
for key, value in props.items():
|
|
148
|
+
key_ = _camel_to_underscore(key)
|
|
149
|
+
if hasattr(obj, key_):
|
|
150
|
+
setattr(obj, key_, value)
|
|
151
|
+
|
|
152
|
+
else:
|
|
153
|
+
_log.debug(
|
|
154
|
+
"object '%s' is missing property '%s'", obj.__class__.__name__, key_
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def dump_message_headers(request):
|
|
159
|
+
bytearr = bytearray()
|
|
160
|
+
headers = request.headers.copy()
|
|
161
|
+
for name, value in headers.items():
|
|
162
|
+
bytearr.extend(format_header(name, value))
|
|
163
|
+
return bytearr.decode("utf-8")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def coerce_to_bytes(data):
|
|
167
|
+
if not isinstance(data, bytes) and hasattr(data, "encode"):
|
|
168
|
+
data = data.encode("utf-8")
|
|
169
|
+
return data
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def format_header(name, value):
|
|
173
|
+
return coerce_to_bytes(name) + b": " + coerce_to_bytes(value) + b"\r\n"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def is_multipart(request):
|
|
177
|
+
headers = dump_message_headers(request)
|
|
178
|
+
if "Content-Type: multipart/form-data" in headers:
|
|
179
|
+
return True
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def deprecated(func):
|
|
184
|
+
"""This is a decorator which can be used to mark functions
|
|
185
|
+
as deprecated. It will result in a warning being emitted
|
|
186
|
+
when the function is used."""
|
|
187
|
+
|
|
188
|
+
@functools.wraps(func)
|
|
189
|
+
def new_func(*args, **kwargs):
|
|
190
|
+
warnings.simplefilter("always", DeprecationWarning) # turn off filter
|
|
191
|
+
warnings.warn(
|
|
192
|
+
f"Call to deprecated function {func.__name__}.",
|
|
193
|
+
category=DeprecationWarning,
|
|
194
|
+
stacklevel=2,
|
|
195
|
+
)
|
|
196
|
+
warnings.simplefilter("default", DeprecationWarning) # reset filter
|
|
197
|
+
return func(*args, **kwargs)
|
|
198
|
+
|
|
199
|
+
return new_func
|