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 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>
@@ -0,0 +1,3 @@
1
+ include requirements.txt
2
+ include README.md
3
+ include LICENSE
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
@@ -0,0 +1,9 @@
1
+ """
2
+ Module for interacting with the EZOffice Inventory API
3
+ """
4
+
5
+ from .assets import *
6
+ from .groups import *
7
+ from .locations import *
8
+ from .members import *
9
+ from .workorders import *