ezoff 0.1.0__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.
- ezoff-0.1.0/LICENSE +24 -0
- ezoff-0.1.0/MANIFEST.in +3 -0
- ezoff-0.1.0/PKG-INFO +50 -0
- ezoff-0.1.0/README.md +38 -0
- ezoff-0.1.0/ezoff/__init__.py +1 -0
- ezoff-0.1.0/ezoff/assets.py +532 -0
- ezoff-0.1.0/ezoff/auth.py +28 -0
- ezoff-0.1.0/ezoff/ezoff.py +9 -0
- ezoff-0.1.0/ezoff/groups.py +74 -0
- ezoff-0.1.0/ezoff/locations.py +349 -0
- ezoff-0.1.0/ezoff/members.py +397 -0
- ezoff-0.1.0/ezoff/workorders.py +448 -0
- ezoff-0.1.0/ezoff.egg-info/PKG-INFO +50 -0
- ezoff-0.1.0/ezoff.egg-info/SOURCES.txt +18 -0
- ezoff-0.1.0/ezoff.egg-info/dependency_links.txt +1 -0
- ezoff-0.1.0/ezoff.egg-info/requires.txt +1 -0
- ezoff-0.1.0/ezoff.egg-info/top_level.txt +1 -0
- ezoff-0.1.0/requirements.txt +1 -0
- ezoff-0.1.0/setup.cfg +4 -0
- ezoff-0.1.0/setup.py +21 -0
ezoff-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <https://unlicense.org>
|
ezoff-0.1.0/MANIFEST.in
ADDED
ezoff-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ezoff
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python package that acts as a wrapper for the EZOffice API.
|
|
5
|
+
Home-page: https://github.com/pepsimidamerica/ezoff
|
|
6
|
+
Author: Jordan Maynor
|
|
7
|
+
Author-email: jmaynor@pepsimidamerica.com
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: requests
|
|
12
|
+
|
|
13
|
+
# ezo
|
|
14
|
+
|
|
15
|
+
Python package for interacting with the EZOffice API
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add as a git submodule for now. Intend on at some point making available as a proper package.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Several environment variables are required for ezo to function.
|
|
24
|
+
|
|
25
|
+
| Required? | Env Variable | Description |
|
|
26
|
+
| --------- | ------------ | ----------- |
|
|
27
|
+
| EZO_BASE_URL | Yes | Should be https://{companyname}.ezofficeinventory.com/ |
|
|
28
|
+
| EZO_TOKEN | Yes | The access token used to authenticate requests |
|
|
29
|
+
|
|
30
|
+
## Project Structure
|
|
31
|
+
|
|
32
|
+
Project is split up into several files depending on what area of the EZOffice API is being dealt with. Purely for organizational purposes.
|
|
33
|
+
|
|
34
|
+
### Assets
|
|
35
|
+
|
|
36
|
+
### Groups
|
|
37
|
+
|
|
38
|
+
### Locations
|
|
39
|
+
|
|
40
|
+
### Members
|
|
41
|
+
|
|
42
|
+
### Work Orders
|
|
43
|
+
|
|
44
|
+
## Notes
|
|
45
|
+
|
|
46
|
+
The documentation is mistaken on custom fields (insofar as filling them out when creating an object or updating the custom field on an already existing object). It says to put underscores in place of spaces in the field name, but this is incorrect. After testing the API, it appears it wants the actual name of the field with the spaces, not underscores. At least on members.
|
|
47
|
+
|
|
48
|
+
Similarly, the documentation isn't exhaustive when it comes to listing the valid fields when creating or updating something. Frequently there are fields on the actual page that aren't mentioned in the EZOffice documentation. Throughout program I check for that keys provided are valid to prevent the API call from erroring out. Just have to add to list of valid key whenever we run into one.
|
|
49
|
+
|
|
50
|
+
When wanting to clear a field out of its current value with an update function, generally the empty string ("") should be used.
|
ezoff-0.1.0/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# ezo
|
|
2
|
+
|
|
3
|
+
Python package for interacting with the EZOffice API
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add as a git submodule for now. Intend on at some point making available as a proper package.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
Several environment variables are required for ezo to function.
|
|
12
|
+
|
|
13
|
+
| Required? | Env Variable | Description |
|
|
14
|
+
| --------- | ------------ | ----------- |
|
|
15
|
+
| EZO_BASE_URL | Yes | Should be https://{companyname}.ezofficeinventory.com/ |
|
|
16
|
+
| EZO_TOKEN | Yes | The access token used to authenticate requests |
|
|
17
|
+
|
|
18
|
+
## Project Structure
|
|
19
|
+
|
|
20
|
+
Project is split up into several files depending on what area of the EZOffice API is being dealt with. Purely for organizational purposes.
|
|
21
|
+
|
|
22
|
+
### Assets
|
|
23
|
+
|
|
24
|
+
### Groups
|
|
25
|
+
|
|
26
|
+
### Locations
|
|
27
|
+
|
|
28
|
+
### Members
|
|
29
|
+
|
|
30
|
+
### Work Orders
|
|
31
|
+
|
|
32
|
+
## Notes
|
|
33
|
+
|
|
34
|
+
The documentation is mistaken on custom fields (insofar as filling them out when creating an object or updating the custom field on an already existing object). It says to put underscores in place of spaces in the field name, but this is incorrect. After testing the API, it appears it wants the actual name of the field with the spaces, not underscores. At least on members.
|
|
35
|
+
|
|
36
|
+
Similarly, the documentation isn't exhaustive when it comes to listing the valid fields when creating or updating something. Frequently there are fields on the actual page that aren't mentioned in the EZOffice documentation. Throughout program I check for that keys provided are valid to prevent the API call from erroring out. Just have to add to list of valid key whenever we run into one.
|
|
37
|
+
|
|
38
|
+
When wanting to clear a field out of its current value with an update function, generally the empty string ("") should be used.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .ezoff import *
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Covers everything related to fixed assets in EZOffice
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from ezoff.auth import Decorators
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@Decorators.check_env_vars
|
|
13
|
+
def get_all_assets() -> list[dict]:
|
|
14
|
+
"""
|
|
15
|
+
Get assets
|
|
16
|
+
Recommended to use endpoint that takes a filter instead.
|
|
17
|
+
This endpoint can be slow as it returns all assets in the system. Potentially
|
|
18
|
+
several hundred pages of assets.
|
|
19
|
+
https://ezo.io/ezofficeinventory/developers/#api-retrive-assets
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
url = os.environ["EZO_BASE_URL"] + "assets.api"
|
|
23
|
+
|
|
24
|
+
page = 1
|
|
25
|
+
all_assets = []
|
|
26
|
+
|
|
27
|
+
while True:
|
|
28
|
+
params = {"page": page}
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
response = requests.get(
|
|
32
|
+
url,
|
|
33
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
34
|
+
params=params,
|
|
35
|
+
data={
|
|
36
|
+
"include_custom_fields": "true",
|
|
37
|
+
"show_document_urls": "true",
|
|
38
|
+
"show_image_urls": "true",
|
|
39
|
+
},
|
|
40
|
+
timeout=10,
|
|
41
|
+
)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print("Error, could not get assets from EZOfficeInventory: ", e)
|
|
44
|
+
raise Exception(
|
|
45
|
+
"Error, could not get assets from EZOfficeInventory: " + str(e)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if response.status_code != 200:
|
|
49
|
+
print(
|
|
50
|
+
f"Error {response.status_code}, could not get assets from EZOfficeInventory: ",
|
|
51
|
+
response.content,
|
|
52
|
+
)
|
|
53
|
+
break
|
|
54
|
+
|
|
55
|
+
data = response.json()
|
|
56
|
+
|
|
57
|
+
if "assets" not in data:
|
|
58
|
+
print(
|
|
59
|
+
f"Error, could not get assets from EZOfficeInventory: ",
|
|
60
|
+
response.content,
|
|
61
|
+
)
|
|
62
|
+
raise Exception(
|
|
63
|
+
f"Error, could not get assets from EZOfficeInventory: "
|
|
64
|
+
+ str(response.content)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
all_assets.extend(data["assets"])
|
|
68
|
+
|
|
69
|
+
if "total_pages" not in data:
|
|
70
|
+
print("Error, could not get total_pages from EZOfficeInventory: ", data)
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
if page >= data["total_pages"]:
|
|
74
|
+
break
|
|
75
|
+
|
|
76
|
+
page += 1
|
|
77
|
+
|
|
78
|
+
return all_assets
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@Decorators.check_env_vars
|
|
82
|
+
def get_filtered_assets(filter: dict) -> list[dict]:
|
|
83
|
+
"""
|
|
84
|
+
Get assets via filtering. Recommended to use this endpoint rather than
|
|
85
|
+
returning all assets.
|
|
86
|
+
"""
|
|
87
|
+
if "status" not in filter:
|
|
88
|
+
raise ValueError("filter must have 'status' key")
|
|
89
|
+
|
|
90
|
+
url = os.environ["EZO_BASE_URL"] + "assets/filter.api"
|
|
91
|
+
|
|
92
|
+
page = 1
|
|
93
|
+
all_assets = []
|
|
94
|
+
|
|
95
|
+
while True:
|
|
96
|
+
params = {"page": page}
|
|
97
|
+
params.update(filter)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
response = requests.get(
|
|
101
|
+
url,
|
|
102
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
103
|
+
params=params,
|
|
104
|
+
data={
|
|
105
|
+
"include_custom_fields": "true",
|
|
106
|
+
"show_document_urls": "true",
|
|
107
|
+
"show_image_urls": "true",
|
|
108
|
+
},
|
|
109
|
+
timeout=10,
|
|
110
|
+
)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print("Error, could not get assets from EZOfficeInventory: ", e)
|
|
113
|
+
raise Exception(
|
|
114
|
+
"Error, could not get assets from EZOfficeInventory: " + str(e)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if response.status_code != 200:
|
|
118
|
+
print(
|
|
119
|
+
f"Error {response.status_code}, could not get assets from EZOfficeInventory: ",
|
|
120
|
+
response.content,
|
|
121
|
+
)
|
|
122
|
+
break
|
|
123
|
+
|
|
124
|
+
data = response.json()
|
|
125
|
+
|
|
126
|
+
if "assets" not in data:
|
|
127
|
+
print(
|
|
128
|
+
f"Error, could not get assets from EZOfficeInventory: ",
|
|
129
|
+
response.content,
|
|
130
|
+
)
|
|
131
|
+
raise Exception(
|
|
132
|
+
f"Error, could not get assets from EZOfficeInventory: "
|
|
133
|
+
+ str(response.content)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
all_assets.extend(data["assets"])
|
|
137
|
+
|
|
138
|
+
if "total_pages" not in data:
|
|
139
|
+
print("Error, could not get total_pages from EZOfficeInventory: ", data)
|
|
140
|
+
break
|
|
141
|
+
|
|
142
|
+
if page >= data["total_pages"]:
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
page += 1
|
|
146
|
+
|
|
147
|
+
return all_assets
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@Decorators.check_env_vars
|
|
151
|
+
def search_for_asset(search_term: str) -> list[dict]:
|
|
152
|
+
"""
|
|
153
|
+
Search for an asset.
|
|
154
|
+
The equivalent of the search bar in the EZOfficeInventory UI.
|
|
155
|
+
May not return all assets that match the search term. Better to use
|
|
156
|
+
get_filtered_assets if you want to return all assets that match a filter.
|
|
157
|
+
https://ezo.io/ezofficeinventory/developers/#api-search-name
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
url = os.environ["EZO_BASE_URL"] + "search.api"
|
|
161
|
+
|
|
162
|
+
page = 1
|
|
163
|
+
all_assets = []
|
|
164
|
+
|
|
165
|
+
while True:
|
|
166
|
+
data = {
|
|
167
|
+
"page": page,
|
|
168
|
+
"search": search_term,
|
|
169
|
+
"facet": "FixedAsset",
|
|
170
|
+
"include_custom_fields": "true",
|
|
171
|
+
"show_document_urls": "true",
|
|
172
|
+
"show_image_urls": "true",
|
|
173
|
+
"show_document_details": "true",
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
response = requests.get(
|
|
178
|
+
url,
|
|
179
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
180
|
+
data=data,
|
|
181
|
+
timeout=10,
|
|
182
|
+
)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print("Error, could not get assets from EZOfficeInventory: ", e)
|
|
185
|
+
raise Exception(
|
|
186
|
+
"Error, could not get assets from EZOfficeInventory: " + str(e)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
if response.status_code != 200:
|
|
190
|
+
print(
|
|
191
|
+
f"Error {response.status_code}, could not get assets from EZOfficeInventory: ",
|
|
192
|
+
response.content,
|
|
193
|
+
)
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
data = response.json()
|
|
197
|
+
|
|
198
|
+
if "assets" not in data:
|
|
199
|
+
print(
|
|
200
|
+
f"Error, could not get assets from EZOfficeInventory: ",
|
|
201
|
+
response.content,
|
|
202
|
+
)
|
|
203
|
+
raise Exception(
|
|
204
|
+
f"Error, could not get assets from EZOfficeInventory: "
|
|
205
|
+
+ str(response.content)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
all_assets.extend(data["assets"])
|
|
209
|
+
|
|
210
|
+
if "total_pages" not in data:
|
|
211
|
+
break
|
|
212
|
+
|
|
213
|
+
if page >= data["total_pages"]:
|
|
214
|
+
break
|
|
215
|
+
|
|
216
|
+
page += 1
|
|
217
|
+
|
|
218
|
+
return all_assets
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@Decorators.check_env_vars
|
|
222
|
+
def create_asset(asset: dict) -> dict:
|
|
223
|
+
"""
|
|
224
|
+
Create an asset
|
|
225
|
+
https://ezo.io/ezofficeinventory/developers/#api-create-asset
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
# Required fields
|
|
229
|
+
if "fixed_asset[name]" not in asset:
|
|
230
|
+
raise ValueError("asset must have 'fixed_asset[name]' key")
|
|
231
|
+
if "fixed_asset[group_id]" not in asset:
|
|
232
|
+
raise ValueError("asset must have 'fixed_asset[group_id]' key")
|
|
233
|
+
if "fixed_asset[purchased_on]" not in asset:
|
|
234
|
+
raise ValueError("asset must have 'fixed_asset[purchased_on]' key")
|
|
235
|
+
# Also check that the date is in the correct format mm/dd/yyyy
|
|
236
|
+
try:
|
|
237
|
+
datetime.strptime(asset["fixed_asset[purchased_on]"], "%m/%d/%Y")
|
|
238
|
+
except ValueError:
|
|
239
|
+
raise ValueError(
|
|
240
|
+
"asset['fixed_asset[purchased_on]'] must be in the format mm/dd/yyyy"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Remove any keys that are not valid
|
|
244
|
+
valid_keys = [
|
|
245
|
+
"fixed_asset[name]",
|
|
246
|
+
"fixed_asset[description]",
|
|
247
|
+
"fixed_asset[group_id]",
|
|
248
|
+
"fixed_asset[sub_group_id]",
|
|
249
|
+
"fixed_asset[purchased_on]",
|
|
250
|
+
"fixed_asset[location_id]",
|
|
251
|
+
"fixed_asset[image_url]",
|
|
252
|
+
"fixed_asset[document_urls][]",
|
|
253
|
+
"fixed_asset[identifier]",
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
asset = {
|
|
257
|
+
k: v for k, v in asset.items() if k in valid_keys or k.startswith("cust_attr")
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
url = os.environ["EZO_BASE_URL"] + "assets.api"
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
response = requests.post(
|
|
264
|
+
url,
|
|
265
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
266
|
+
data=asset,
|
|
267
|
+
timeout=10,
|
|
268
|
+
)
|
|
269
|
+
except Exception as e:
|
|
270
|
+
print("Error, could not create asset in EZOfficeInventory: ", e)
|
|
271
|
+
raise Exception("Error, could not create asset in EZOfficeInventory: " + str(e))
|
|
272
|
+
|
|
273
|
+
if response.status_code != 200:
|
|
274
|
+
print(
|
|
275
|
+
f"Error {response.status_code}, could not create asset in EZOfficeInventory: ",
|
|
276
|
+
response.content,
|
|
277
|
+
)
|
|
278
|
+
raise Exception(
|
|
279
|
+
f"Error {response.status_code}, could not create asset in EZOfficeInventory: "
|
|
280
|
+
+ str(response.content)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return response.json()
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@Decorators.check_env_vars
|
|
287
|
+
def update_asset(asset_id: int, asset: dict) -> dict:
|
|
288
|
+
"""
|
|
289
|
+
Update an asset's details
|
|
290
|
+
https://ezo.io/ezofficeinventory/developers/#api-update-asset
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
# Remove any keys that are not valid
|
|
294
|
+
valid_keys = [
|
|
295
|
+
"fixed_asset[name]",
|
|
296
|
+
"fixed_asset[description]",
|
|
297
|
+
"fixed_asset[group_id]",
|
|
298
|
+
"fixed_asset[sub_group_id]",
|
|
299
|
+
"fixed_asset[identifier]",
|
|
300
|
+
"fixed_asset[purchased_on]",
|
|
301
|
+
"fixed_asset[location_id]",
|
|
302
|
+
"fixed_asset[image_url]",
|
|
303
|
+
"fixed_asset[document_urls][]",
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
asset = {
|
|
307
|
+
k: v for k, v in asset.items() if k in valid_keys or k.startswith("cust_attr")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
url = os.environ["EZO_BASE_URL"] + "assets/" + str(asset_id) + ".api"
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
response = requests.put(
|
|
314
|
+
url,
|
|
315
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
316
|
+
data=asset,
|
|
317
|
+
timeout=10,
|
|
318
|
+
)
|
|
319
|
+
except Exception as e:
|
|
320
|
+
print("Error, could not update asset in EZOfficeInventory: ", e)
|
|
321
|
+
raise Exception("Error, could not update asset in EZOfficeInventory: " + str(e))
|
|
322
|
+
|
|
323
|
+
if response.status_code != 200:
|
|
324
|
+
print(
|
|
325
|
+
f"Error {response.status_code}, could not update asset in EZOfficeInventory: ",
|
|
326
|
+
response.content,
|
|
327
|
+
)
|
|
328
|
+
raise Exception(
|
|
329
|
+
f"Error {response.status_code}, could not update asset in EZOfficeInventory: "
|
|
330
|
+
+ str(response.content)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
return response.json()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@Decorators.check_env_vars
|
|
337
|
+
def delete_asset(asset_id: int) -> dict:
|
|
338
|
+
"""
|
|
339
|
+
Delete an asset
|
|
340
|
+
https://ezo.io/ezofficeinventory/developers/#api-delete-asset
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
url = os.environ["EZO_BASE_URL"] + "assets/" + str(asset_id) + ".api"
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
response = requests.delete(
|
|
347
|
+
url,
|
|
348
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
349
|
+
timeout=10,
|
|
350
|
+
)
|
|
351
|
+
except Exception as e:
|
|
352
|
+
print("Error, could not delete asset in EZOfficeInventory: ", e)
|
|
353
|
+
raise Exception("Error, could not delete asset in EZOfficeInventory: " + str(e))
|
|
354
|
+
|
|
355
|
+
if response.status_code != 200:
|
|
356
|
+
print(
|
|
357
|
+
f"Error {response.status_code}, could not delete asset in EZOfficeInventory: ",
|
|
358
|
+
response.content,
|
|
359
|
+
)
|
|
360
|
+
raise Exception(
|
|
361
|
+
f"Error {response.status_code}, could not delete asset in EZOfficeInventory: "
|
|
362
|
+
+ str(response.content)
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
return response.json()
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@Decorators.check_env_vars
|
|
369
|
+
def checkin_asset(asset_id: int, checkin: dict) -> dict:
|
|
370
|
+
"""
|
|
371
|
+
Check in an asset to a location
|
|
372
|
+
https://ezo.io/ezofficeinventory/developers/#api-checkin-asset
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
# Required fields
|
|
376
|
+
if "checkin_values[location_id]" not in checkin:
|
|
377
|
+
raise ValueError("checkin must have 'checkin[location_id]' key")
|
|
378
|
+
|
|
379
|
+
# Remove any keys that are not valid
|
|
380
|
+
valid_keys = [
|
|
381
|
+
"checkin_values[location_id]",
|
|
382
|
+
"checkin_values[comments]",
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
checkin = {
|
|
386
|
+
k: v
|
|
387
|
+
for k, v in checkin.items()
|
|
388
|
+
if k in valid_keys or k.startswith("checkin_values[c_attr_vals]")
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
url = os.environ["EZO_BASE_URL"] + "assets/" + str(asset_id) + "/checkin.api"
|
|
392
|
+
|
|
393
|
+
try:
|
|
394
|
+
response = requests.put(
|
|
395
|
+
url,
|
|
396
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
397
|
+
data=checkin,
|
|
398
|
+
timeout=10,
|
|
399
|
+
)
|
|
400
|
+
except Exception as e:
|
|
401
|
+
print("Error, could not checkin asset in EZOfficeInventory: ", e)
|
|
402
|
+
raise Exception(
|
|
403
|
+
"Error, could not checkin asset in EZOfficeInventory: " + str(e)
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
if response.status_code != 200:
|
|
407
|
+
print(
|
|
408
|
+
f"Error {response.status_code}, could not checkin asset in EZOfficeInventory: ",
|
|
409
|
+
response.content,
|
|
410
|
+
)
|
|
411
|
+
raise Exception(
|
|
412
|
+
f"Error {response.status_code}, could not checkin asset in EZOfficeInventory: "
|
|
413
|
+
+ str(response.content)
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
return response.json()
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
@Decorators.check_env_vars
|
|
420
|
+
def checkout_asset(asset_id: int, user_id: int, checkout: dict) -> dict:
|
|
421
|
+
"""
|
|
422
|
+
Check out an asset to a member
|
|
423
|
+
https://ezo.io/ezofficeinventory/developers/#api-checkout-asset
|
|
424
|
+
|
|
425
|
+
Note: If user is inactive, checkout will return a 200 status code but the
|
|
426
|
+
asset will not be checked out. Response will contain a message.
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
# Remove any keys that are not valid
|
|
430
|
+
valid_keys = [
|
|
431
|
+
"checkout_values[location_id]",
|
|
432
|
+
"checkout_values[comments]",
|
|
433
|
+
"till",
|
|
434
|
+
"till_time",
|
|
435
|
+
"checkout_values[override_conflicting_reservations]",
|
|
436
|
+
"checkout_values[override_my_conflicting_reservations]",
|
|
437
|
+
]
|
|
438
|
+
|
|
439
|
+
checkout = {
|
|
440
|
+
k: v
|
|
441
|
+
for k, v in checkout.items()
|
|
442
|
+
if k in valid_keys or k.startswith("checkout_values[c_attr_vals]")
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
url = os.environ["EZO_BASE_URL"] + "assets/" + str(asset_id) + "/checkout.api"
|
|
446
|
+
|
|
447
|
+
try:
|
|
448
|
+
response = requests.put(
|
|
449
|
+
url,
|
|
450
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
451
|
+
params={"user_id": user_id},
|
|
452
|
+
data=checkout,
|
|
453
|
+
timeout=10,
|
|
454
|
+
)
|
|
455
|
+
except Exception as e:
|
|
456
|
+
print("Error, could not checkout asset in EZOfficeInventory: ", e)
|
|
457
|
+
raise Exception(
|
|
458
|
+
"Error, could not checkout asset in EZOfficeInventory: " + str(e)
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if response.status_code != 200:
|
|
462
|
+
print(
|
|
463
|
+
f"Error {response.status_code}, could not checkout asset in EZOfficeInventory: ",
|
|
464
|
+
response.content,
|
|
465
|
+
)
|
|
466
|
+
raise Exception(
|
|
467
|
+
f"Error {response.status_code}, could not checkout asset in EZOfficeInventory: "
|
|
468
|
+
+ str(response.content)
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
return response.json()
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
@Decorators.check_env_vars
|
|
475
|
+
def get_asset_history(asset_id: int) -> list[dict]:
|
|
476
|
+
"""
|
|
477
|
+
Get asset history
|
|
478
|
+
https://ezo.io/ezofficeinventory/developers/#api-checkin-out-history
|
|
479
|
+
"""
|
|
480
|
+
|
|
481
|
+
url = (
|
|
482
|
+
os.environ["EZO_BASE_URL"] + "assets/" + str(asset_id) + "/history_paginate.api"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
page = 1
|
|
486
|
+
all_history = []
|
|
487
|
+
|
|
488
|
+
while True:
|
|
489
|
+
try:
|
|
490
|
+
response = requests.get(
|
|
491
|
+
url,
|
|
492
|
+
headers={"Authorization": "Bearer " + os.environ["EZO_TOKEN"]},
|
|
493
|
+
params={"page": page},
|
|
494
|
+
timeout=10,
|
|
495
|
+
)
|
|
496
|
+
except Exception as e:
|
|
497
|
+
print("Error, could not get asset history from EZOfficeInventory: ", e)
|
|
498
|
+
raise Exception(
|
|
499
|
+
"Error, could not get asset history from EZOfficeInventory: " + str(e)
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
if response.status_code != 200:
|
|
503
|
+
print(
|
|
504
|
+
f"Error {response.status_code}, could not get asset history from EZOfficeInventory: ",
|
|
505
|
+
response.content,
|
|
506
|
+
)
|
|
507
|
+
break
|
|
508
|
+
|
|
509
|
+
data = response.json()
|
|
510
|
+
|
|
511
|
+
if "history" not in data:
|
|
512
|
+
print(
|
|
513
|
+
f"Error, could not get asset history from EZOfficeInventory: ",
|
|
514
|
+
response.content,
|
|
515
|
+
)
|
|
516
|
+
raise Exception(
|
|
517
|
+
f"Error, could not get asset history from EZOfficeInventory: "
|
|
518
|
+
+ str(response.content)
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
all_history.extend(data["history"])
|
|
522
|
+
|
|
523
|
+
if "total_pages" not in data:
|
|
524
|
+
print("Error, could not get total_pages from EZOfficeInventory: ", data)
|
|
525
|
+
break
|
|
526
|
+
|
|
527
|
+
if page >= data["total_pages"]:
|
|
528
|
+
break
|
|
529
|
+
|
|
530
|
+
page += 1
|
|
531
|
+
|
|
532
|
+
return all_history
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handles any authentication-realted functionality.
|
|
3
|
+
Largely just checking if the required environment variables are set.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Decorators:
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def check_env_vars(decorated):
|
|
13
|
+
"""
|
|
14
|
+
Decorator to check if the required environment variables are set
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def wrapper(*args, **kwargs):
|
|
18
|
+
"""
|
|
19
|
+
Wrapper function
|
|
20
|
+
"""
|
|
21
|
+
if "EZO_BASE_URL" not in os.environ:
|
|
22
|
+
raise Exception("EZO_BASE_URL not found in environment variables.")
|
|
23
|
+
if "EZO_TOKEN" not in os.environ:
|
|
24
|
+
raise Exception("EZO_TOKEN not found in environment variables.")
|
|
25
|
+
return decorated(*args, **kwargs)
|
|
26
|
+
|
|
27
|
+
wrapper.__name__ = decorated.__name__
|
|
28
|
+
return wrapper
|