smartsheet-tools 0.0.2__tar.gz → 0.0.5__tar.gz
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_tools-0.0.2/smartsheet_tools.egg-info → smartsheet_tools-0.0.5}/PKG-INFO +1 -1
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/pyproject.toml +1 -1
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/setup.py +1 -1
- smartsheet_tools-0.0.5/smartsheet_tools/__init__.py +176 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5/smartsheet_tools.egg-info}/PKG-INFO +1 -1
- smartsheet_tools-0.0.2/smartsheet_tools/__init__.py +0 -56
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/LICENSE +0 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/README.md +0 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/setup.cfg +0 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/smartsheet_tools.egg-info/SOURCES.txt +0 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/smartsheet_tools.egg-info/dependency_links.txt +0 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/smartsheet_tools.egg-info/requires.txt +0 -0
- {smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/smartsheet_tools.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smartsheet_tools
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: A collection of convenience functions to aid with transitioning from simple-smartsheet to the SDK API and common tasks
|
|
5
5
|
Author: Ashton Pooley
|
|
6
6
|
Author-email: Ashton Pooley <ashton@ashi.digital>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "smartsheet_tools" # from setup.py
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.5" # from setup.py
|
|
8
8
|
description = "A collection of convenience functions to aid with transitioning from simple-smartsheet to the SDK API and common tasks"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9" # matches classifiers (3.9–3.12)
|
|
@@ -2,7 +2,7 @@ from setuptools import setup
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="smartsheet_tools",
|
|
5
|
-
version="0.0.
|
|
5
|
+
version="0.0.5",
|
|
6
6
|
description="A collection of convenience functions to aid with transitioning from simple-smartsheet to the SDK API and common tasks",
|
|
7
7
|
author="Ashton Pooley",
|
|
8
8
|
author_email="Ashton@ashi.digital",
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import re
|
|
3
|
+
from smartsheet.models import Cell, Row, Folder, Sheet
|
|
4
|
+
from smartsheet.models import Column
|
|
5
|
+
|
|
6
|
+
# Cache for column types to minimize API calls when correcting date formats
|
|
7
|
+
_COLUMN_TYPE_CACHE = {}
|
|
8
|
+
_TITLE_TO_ID_CACHE = {}
|
|
9
|
+
_ID_TO_INDEX_CACHE = {}
|
|
10
|
+
|
|
11
|
+
def norm(v):
|
|
12
|
+
if v is None:
|
|
13
|
+
return ""
|
|
14
|
+
s = str(v).strip().lower()
|
|
15
|
+
return re.sub(r"\.0+$", "", s)
|
|
16
|
+
|
|
17
|
+
def disp_or_val(cell):
|
|
18
|
+
# prefer display_value when Smartsheet provides a formatted value
|
|
19
|
+
dv = getattr(cell, "display_value", None)
|
|
20
|
+
return dv if dv not in (None, "") else cell.value
|
|
21
|
+
|
|
22
|
+
def title_to_index(sheet):
|
|
23
|
+
# authoritative positions from Smartsheet (not Python enumerate order)
|
|
24
|
+
if sheet.id not in _TITLE_TO_ID_CACHE:
|
|
25
|
+
_TITLE_TO_ID_CACHE[sheet.id] = {c.title: c.index for c in sheet.columns}
|
|
26
|
+
return _TITLE_TO_ID_CACHE[sheet.id]
|
|
27
|
+
|
|
28
|
+
def index_to_id(sheet):
|
|
29
|
+
# authoritative positions from Smartsheet (not Python enumerate order)
|
|
30
|
+
if sheet.id not in _ID_TO_INDEX_CACHE:
|
|
31
|
+
_ID_TO_INDEX_CACHE[sheet.id] = {c.index: c.id for c in sheet.columns}
|
|
32
|
+
return _ID_TO_INDEX_CACHE[sheet.id]
|
|
33
|
+
|
|
34
|
+
def id_to_index(sheet):
|
|
35
|
+
# authoritative positions from Smartsheet (not Python enumerate order)
|
|
36
|
+
return {c.id: c.index for c in sheet.columns}
|
|
37
|
+
|
|
38
|
+
def id_to_title(sheet):
|
|
39
|
+
return {c.id: c.title for c in sheet.columns}
|
|
40
|
+
|
|
41
|
+
def title_to_id(sheet):
|
|
42
|
+
return {c.title: c.id for c in sheet.columns}
|
|
43
|
+
|
|
44
|
+
def guard_row(row, *idxs):
|
|
45
|
+
# ensure row has enough cells for all requested positions
|
|
46
|
+
return max(idxs) < len(row.cells)
|
|
47
|
+
|
|
48
|
+
def datetime_to_isoformat(dt):
|
|
49
|
+
if dt is None:
|
|
50
|
+
return None
|
|
51
|
+
return dt.replace(microsecond=0).isoformat() + 'Z'
|
|
52
|
+
|
|
53
|
+
def standard_time_to_isoformat(st):
|
|
54
|
+
if st is None:
|
|
55
|
+
return None
|
|
56
|
+
return datetime_to_isoformat(datetime.strptime(st, "%m/%d/%Y"))
|
|
57
|
+
|
|
58
|
+
def get_cached_column_type(column_id, sheet_obj):
|
|
59
|
+
if sheet_obj.id not in _COLUMN_TYPE_CACHE:
|
|
60
|
+
_COLUMN_TYPE_CACHE[sheet_obj.id] = {}
|
|
61
|
+
|
|
62
|
+
if column_id not in _COLUMN_TYPE_CACHE[sheet_obj.id]:
|
|
63
|
+
_COLUMN_TYPE_CACHE[sheet_obj.id][column_id] = str(sheet_obj.get_column(column_id).type)
|
|
64
|
+
|
|
65
|
+
return _COLUMN_TYPE_CACHE[sheet_obj.id][column_id]
|
|
66
|
+
|
|
67
|
+
def get_col_names_of_date_cols(sheet_obj):
|
|
68
|
+
return [c.title for c in sheet_obj.columns if get_cached_column_type(c.id, sheet_obj) in ("DATE", "DATETIME")]
|
|
69
|
+
|
|
70
|
+
def brute_force_date_string(s, nonetype_if_fail=False):
|
|
71
|
+
# attempt to parse a date string in common formats to ISO 8601
|
|
72
|
+
if isinstance(s, datetime):
|
|
73
|
+
return datetime_to_isoformat(s)
|
|
74
|
+
|
|
75
|
+
if not isinstance(s, str):
|
|
76
|
+
return None if nonetype_if_fail else s
|
|
77
|
+
|
|
78
|
+
s = s.split(" ")[0]
|
|
79
|
+
for fmt in ("%Y-%m-%d", "%m/%d/%Y", "%m/%d/%y"):
|
|
80
|
+
try:
|
|
81
|
+
return datetime_to_isoformat(datetime.strptime(s, fmt))
|
|
82
|
+
except ValueError:
|
|
83
|
+
continue
|
|
84
|
+
return None if nonetype_if_fail else s
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_date_col(column_id, sheet_obj):
|
|
88
|
+
column_type = get_cached_column_type(column_id, sheet_obj)
|
|
89
|
+
return column_type in ("DATE", "DATETIME")
|
|
90
|
+
|
|
91
|
+
def correct_date_format(isoformat_datetime, column_id, sheet_obj):
|
|
92
|
+
if isinstance(isoformat_datetime, datetime):
|
|
93
|
+
isoformat_datetime = datetime_to_isoformat(isoformat_datetime)
|
|
94
|
+
|
|
95
|
+
column_type = get_cached_column_type(column_id, sheet_obj)
|
|
96
|
+
if column_type == "DATE":
|
|
97
|
+
return isoformat_datetime.split("T",1)[0]
|
|
98
|
+
elif column_type == "DATETIME":
|
|
99
|
+
return isoformat_datetime
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
def new_cell(column_id=None, value=None, strict=False, formula=None):
|
|
103
|
+
new_cell = Cell()
|
|
104
|
+
if column_id is not None:
|
|
105
|
+
new_cell.column_id = column_id
|
|
106
|
+
if formula is not None:
|
|
107
|
+
new_cell.formula = formula
|
|
108
|
+
else:
|
|
109
|
+
new_cell.value = value
|
|
110
|
+
new_cell.strict = strict
|
|
111
|
+
return new_cell
|
|
112
|
+
|
|
113
|
+
def new_row(cells=None, id=None, parent_id=None, to_top=False, locked=False):
|
|
114
|
+
new_row = Row()
|
|
115
|
+
if cells:
|
|
116
|
+
new_row.cells = cells
|
|
117
|
+
if id:
|
|
118
|
+
new_row.id = id
|
|
119
|
+
if parent_id:
|
|
120
|
+
new_row.parent_id = parent_id
|
|
121
|
+
if to_top:
|
|
122
|
+
new_row.to_top = to_top
|
|
123
|
+
if locked:
|
|
124
|
+
new_row.locked = locked
|
|
125
|
+
return new_row
|
|
126
|
+
|
|
127
|
+
def walk_folder_for_sheets(smartsheet_client, folder_id):
|
|
128
|
+
for item in smartsheet_client.Folders.get_folder_children(folder_id).data:
|
|
129
|
+
if isinstance(item, Folder):
|
|
130
|
+
yield from walk_folder_for_sheets(smartsheet_client, item.id)
|
|
131
|
+
elif isinstance(item, Sheet):
|
|
132
|
+
yield item
|
|
133
|
+
|
|
134
|
+
def walk_workspace_for_sheets(smartsheet_client, workspace_id):
|
|
135
|
+
for item in smartsheet_client.Workspaces.get_workspace_children(workspace_id).data:
|
|
136
|
+
if isinstance(item, Folder):
|
|
137
|
+
yield from walk_folder_for_sheets(smartsheet_client, item.id)
|
|
138
|
+
elif isinstance(item, Sheet):
|
|
139
|
+
yield item
|
|
140
|
+
|
|
141
|
+
def walk_folder_for_folders(smartsheet_client, folder_id):
|
|
142
|
+
for item in smartsheet_client.Folders.get_folder_children(folder_id).data:
|
|
143
|
+
if isinstance(item, Folder):
|
|
144
|
+
yield item
|
|
145
|
+
yield from walk_folder_for_folders(smartsheet_client, item.id)
|
|
146
|
+
|
|
147
|
+
def walk_workspace_for_folders(smartsheet_client, workspace_id):
|
|
148
|
+
for item in smartsheet_client.Workspaces.get_workspace_children(workspace_id).data:
|
|
149
|
+
if isinstance(item, Folder):
|
|
150
|
+
yield item
|
|
151
|
+
yield from walk_folder_for_folders(smartsheet_client, item.id)
|
|
152
|
+
|
|
153
|
+
def walk_sheet_names_from_workspace(smartsheet_client, workspace_id):
|
|
154
|
+
for sheet in walk_workspace_for_sheets(smartsheet_client, workspace_id):
|
|
155
|
+
yield sheet.name
|
|
156
|
+
|
|
157
|
+
def new_column(column_type, title, index=None, id=None, options=None, symbol=None, primary=False, hidden=False, locked=False):
|
|
158
|
+
new_column = Column()
|
|
159
|
+
|
|
160
|
+
new_column.type = column_type
|
|
161
|
+
new_column.title = title
|
|
162
|
+
if index is not None:
|
|
163
|
+
new_column.index = index
|
|
164
|
+
if id is not None:
|
|
165
|
+
new_column.id = id
|
|
166
|
+
if options is not None and column_type in ("PICKLIST", "MULTI_PICKLIST"):
|
|
167
|
+
new_column.options = options
|
|
168
|
+
if symbol is not None and column_type == "CHECKBOX":
|
|
169
|
+
new_column.symbol = symbol
|
|
170
|
+
if primary:
|
|
171
|
+
new_column.primary = True
|
|
172
|
+
if hidden:
|
|
173
|
+
new_column.hidden = True
|
|
174
|
+
if locked:
|
|
175
|
+
new_column.locked = True
|
|
176
|
+
return new_column
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smartsheet_tools
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: A collection of convenience functions to aid with transitioning from simple-smartsheet to the SDK API and common tasks
|
|
5
5
|
Author: Ashton Pooley
|
|
6
6
|
Author-email: Ashton Pooley <ashton@ashi.digital>
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from smartsheet.models import Cell, Row
|
|
3
|
-
|
|
4
|
-
def norm(v):
|
|
5
|
-
if v is None:
|
|
6
|
-
return ""
|
|
7
|
-
s = str(v).strip().lower()
|
|
8
|
-
return re.sub(r"\.0+$", "", s)
|
|
9
|
-
|
|
10
|
-
def disp_or_val(cell):
|
|
11
|
-
# prefer display_value when Smartsheet provides a formatted value
|
|
12
|
-
dv = getattr(cell, "display_value", None)
|
|
13
|
-
return dv if dv not in (None, "") else cell.value
|
|
14
|
-
|
|
15
|
-
def title_to_index(sheet):
|
|
16
|
-
# authoritative positions from Smartsheet (not Python enumerate order)
|
|
17
|
-
return {c.title: c.index for c in sheet.columns}
|
|
18
|
-
|
|
19
|
-
def index_to_id(sheet):
|
|
20
|
-
# authoritative positions from Smartsheet (not Python enumerate order)
|
|
21
|
-
return {c.index: c.id for c in sheet.columns}
|
|
22
|
-
|
|
23
|
-
def id_to_index(sheet):
|
|
24
|
-
# authoritative positions from Smartsheet (not Python enumerate order)
|
|
25
|
-
return {c.id: c.index for c in sheet.columns}
|
|
26
|
-
|
|
27
|
-
def id_to_title(sheet):
|
|
28
|
-
return {c.id: c.title for c in sheet.columns}
|
|
29
|
-
|
|
30
|
-
def title_to_id(sheet):
|
|
31
|
-
return {c.title: c.id for c in sheet.columns}
|
|
32
|
-
|
|
33
|
-
def guard_row(row, *idxs):
|
|
34
|
-
# ensure row has enough cells for all requested positions
|
|
35
|
-
return max(idxs) < len(row.cells)
|
|
36
|
-
|
|
37
|
-
def new_cell(column_id, value=None, strict=False, formula=None):
|
|
38
|
-
new_cell = Cell()
|
|
39
|
-
new_cell.column_id = column_id
|
|
40
|
-
if formula is not None:
|
|
41
|
-
new_cell.formula = formula
|
|
42
|
-
else:
|
|
43
|
-
new_cell.value = value
|
|
44
|
-
if strict:
|
|
45
|
-
new_cell.strict = True
|
|
46
|
-
return new_cell
|
|
47
|
-
|
|
48
|
-
def new_row(cells=None, parent_id=None, to_top=False):
|
|
49
|
-
new_row = Row()
|
|
50
|
-
if cells:
|
|
51
|
-
new_row.cells = cells
|
|
52
|
-
if parent_id:
|
|
53
|
-
new_row.parent_id = parent_id
|
|
54
|
-
if to_top:
|
|
55
|
-
new_row.to_top = to_top
|
|
56
|
-
return new_row
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{smartsheet_tools-0.0.2 → smartsheet_tools-0.0.5}/smartsheet_tools.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|