libib-client 1.0.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.
- libib_client-1.0.0/LICENSE +21 -0
- libib_client-1.0.0/PKG-INFO +44 -0
- libib_client-1.0.0/README.md +28 -0
- libib_client-1.0.0/pyproject.toml +27 -0
- libib_client-1.0.0/setup.cfg +4 -0
- libib_client-1.0.0/src/libib-client/__init__.py +4 -0
- libib_client-1.0.0/src/libib-client/main.py +454 -0
- libib_client-1.0.0/src/libib_client.egg-info/PKG-INFO +44 -0
- libib_client-1.0.0/src/libib_client.egg-info/SOURCES.txt +10 -0
- libib_client-1.0.0/src/libib_client.egg-info/dependency_links.txt +1 -0
- libib_client-1.0.0/src/libib_client.egg-info/requires.txt +1 -0
- libib_client-1.0.0/src/libib_client.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Masarik
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: libib-client
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A synchronous, third-party client for the Libib API
|
|
5
|
+
Author: Michael Masarik
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://michael-masarik.github.io/libib-client/
|
|
8
|
+
Project-URL: Repository, https://github.com/michael-masarik/libib-client
|
|
9
|
+
Project-URL: Issues, https://github.com/michael-masarik/libib-client/issues
|
|
10
|
+
Keywords: libib,library,api,client
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: requests>=2.31.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# Libib-Client
|
|
18
|
+
A synchronous, third-party client for the [Libib API](https://support.libib.com/rest-api/introduction.html)
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
```shell
|
|
22
|
+
pip install libib-client
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
To initalize the client:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import Libib
|
|
31
|
+
|
|
32
|
+
# For Pro accounts
|
|
33
|
+
client = Libib("your-api-key", "your-user-id")
|
|
34
|
+
|
|
35
|
+
# For Ultimate accounts, also pass your Ultimate ID
|
|
36
|
+
client = Libib("your-api-key", "your-user-id", "your-ultimate-id")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Documentation:
|
|
40
|
+
Documentaion can be [found here] (https://michael-masarik.github.io/libib-client/)
|
|
41
|
+
|
|
42
|
+
## Note
|
|
43
|
+
|
|
44
|
+
I do not have an Ultimate account, so if the Ultimate features (or any features, for that matter) do not work, feel free to open an issue or a PR
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Libib-Client
|
|
2
|
+
A synchronous, third-party client for the [Libib API](https://support.libib.com/rest-api/introduction.html)
|
|
3
|
+
|
|
4
|
+
## Install
|
|
5
|
+
```shell
|
|
6
|
+
pip install libib-client
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
To initalize the client:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import Libib
|
|
15
|
+
|
|
16
|
+
# For Pro accounts
|
|
17
|
+
client = Libib("your-api-key", "your-user-id")
|
|
18
|
+
|
|
19
|
+
# For Ultimate accounts, also pass your Ultimate ID
|
|
20
|
+
client = Libib("your-api-key", "your-user-id", "your-ultimate-id")
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Documentation:
|
|
24
|
+
Documentaion can be [found here] (https://michael-masarik.github.io/libib-client/)
|
|
25
|
+
|
|
26
|
+
## Note
|
|
27
|
+
|
|
28
|
+
I do not have an Ultimate account, so if the Ultimate features (or any features, for that matter) do not work, feel free to open an issue or a PR
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "libib-client"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A synchronous, third-party client for the Libib API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Michael Masarik" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["libib", "library", "api", "client"]
|
|
16
|
+
dependencies = ["requests>=2.31.0"]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://michael-masarik.github.io/libib-client/"
|
|
20
|
+
Repository = "https://github.com/michael-masarik/libib-client"
|
|
21
|
+
Issues = "https://github.com/michael-masarik/libib-client/issues"
|
|
22
|
+
|
|
23
|
+
[tool.black]
|
|
24
|
+
line-length = 101
|
|
25
|
+
[tool.isort]
|
|
26
|
+
profile = "black"
|
|
27
|
+
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# Docs: https://support.libib.com/rest-api/introduction.html
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
RATE = 2
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Libib:
|
|
10
|
+
"""
|
|
11
|
+
Simple, Third-Party API Client for Libib
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self, apiKey: str, apiUserID: str, ultimateID: str | None = None) -> None:
|
|
14
|
+
self.apiHeaders = {}
|
|
15
|
+
self.apiHeaders["x-api-key"] = apiKey
|
|
16
|
+
self.apiHeaders["x-api-user"] = apiUserID
|
|
17
|
+
if ultimateID:
|
|
18
|
+
self.apiHeaders["x-api-ultimate"] = ultimateID
|
|
19
|
+
self.patrons = Patrons(self)
|
|
20
|
+
self.managers = Managers(self)
|
|
21
|
+
self.accounts = Accounts(self)
|
|
22
|
+
|
|
23
|
+
class Patrons:
|
|
24
|
+
"""
|
|
25
|
+
Patron subclient
|
|
26
|
+
"""
|
|
27
|
+
url = "https://api.libib.com/patrons"
|
|
28
|
+
patron_sig = {"barcode","first_name","last_name","email","notification_emails","tags","patron_id","phone","address1","address2","city","state","country","zip","freeze","password"}
|
|
29
|
+
|
|
30
|
+
def __init__(self, client: Libib) -> None:
|
|
31
|
+
self.client = client
|
|
32
|
+
self.headers = client.apiHeaders
|
|
33
|
+
|
|
34
|
+
def get_patrons(self) -> dict | list:
|
|
35
|
+
"""
|
|
36
|
+
Retrieve a list of all existing patrons on your site. Returns 50 patrons at a time with pagination support.
|
|
37
|
+
For lists of patrons greater than 50, this function auto-handles pagination (with a 2 second pause between calls)
|
|
38
|
+
|
|
39
|
+
:param self: `Self@Libib`
|
|
40
|
+
|
|
41
|
+
:return success:
|
|
42
|
+
```python
|
|
43
|
+
[
|
|
44
|
+
{
|
|
45
|
+
"barcode": "2020000000013",
|
|
46
|
+
"first_name": "Mary",
|
|
47
|
+
"last_name": "Shelley",
|
|
48
|
+
"email": "frankenstein@example.com",
|
|
49
|
+
"notification_emails": None,
|
|
50
|
+
"tags": None,
|
|
51
|
+
"patron_id": "mshelley",
|
|
52
|
+
"phone": "555-123-4567",
|
|
53
|
+
"address1": None,
|
|
54
|
+
"address2": None,
|
|
55
|
+
"city": "Augusta",
|
|
56
|
+
"state": "KS",
|
|
57
|
+
"country": "US",
|
|
58
|
+
"zip": None,
|
|
59
|
+
"freeze": None,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"barcode": "2020000000037",
|
|
63
|
+
"first_name": "Marcus",
|
|
64
|
+
"last_name": "Aurelius",
|
|
65
|
+
"email": "meditations@example.com",
|
|
66
|
+
"notification_emails": None,
|
|
67
|
+
"tags": None,
|
|
68
|
+
"patron_id": None,
|
|
69
|
+
"phone": None,
|
|
70
|
+
"address1": None,
|
|
71
|
+
"address2": None,
|
|
72
|
+
"city": None,
|
|
73
|
+
"state": None,
|
|
74
|
+
"country": "US",
|
|
75
|
+
"zip": None,
|
|
76
|
+
"freeze": 1,
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
```
|
|
80
|
+
:return error:
|
|
81
|
+
```python
|
|
82
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
83
|
+
```
|
|
84
|
+
"""
|
|
85
|
+
params = {
|
|
86
|
+
'page': 1
|
|
87
|
+
}
|
|
88
|
+
response = requests.get(self.url, headers=self.headers, params=params)
|
|
89
|
+
data = response.json()
|
|
90
|
+
if response.status_code != 200:
|
|
91
|
+
return {"status": "error", "code": response.status_code, "body": data}
|
|
92
|
+
current_page = 1
|
|
93
|
+
pages_to_go = data.get("pages", 1)
|
|
94
|
+
patrons = []
|
|
95
|
+
errors = None
|
|
96
|
+
|
|
97
|
+
for patron in data.get("patrons", []):
|
|
98
|
+
patrons.append(patron)
|
|
99
|
+
|
|
100
|
+
while current_page < pages_to_go:
|
|
101
|
+
time.sleep(RATE) # Pause to avoid rate limiting
|
|
102
|
+
params["page"] += 1
|
|
103
|
+
response = requests.get(self.url, headers=self.headers, params=params)
|
|
104
|
+
data = response.json()
|
|
105
|
+
if response.status_code != 200:
|
|
106
|
+
errors = {"status": "error", "code": response.status_code, "body": data}
|
|
107
|
+
break
|
|
108
|
+
for patron in data.get("patrons", []):
|
|
109
|
+
patrons.append(patron)
|
|
110
|
+
current_page += 1
|
|
111
|
+
if errors:
|
|
112
|
+
return errors
|
|
113
|
+
return patrons
|
|
114
|
+
|
|
115
|
+
def get_patron_by_id(self, identifier: str) -> dict:
|
|
116
|
+
"""
|
|
117
|
+
Retrieve a single patron by passing the patron's barcode or email as an identifier.
|
|
118
|
+
|
|
119
|
+
:param self: `Self@Libib`
|
|
120
|
+
:param identifier: patron email/barcode
|
|
121
|
+
:type identifier: str
|
|
122
|
+
:return success:
|
|
123
|
+
```python
|
|
124
|
+
{
|
|
125
|
+
"barcode": "2020000000013",
|
|
126
|
+
"first_name": "Mary",
|
|
127
|
+
"last_name": "Shelley",
|
|
128
|
+
"email": "frankenstein@example.com",
|
|
129
|
+
"notification_emails": None,
|
|
130
|
+
"tags": None,
|
|
131
|
+
"patron_id": "mshelley",
|
|
132
|
+
"phone": "555-123-4567",
|
|
133
|
+
"address1": None,
|
|
134
|
+
"address2": None,
|
|
135
|
+
"city": "Augusta",
|
|
136
|
+
"state": "KS",
|
|
137
|
+
"country": "US",
|
|
138
|
+
"zip": None,
|
|
139
|
+
"freeze": None,
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
:return error:
|
|
143
|
+
```python
|
|
144
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
145
|
+
```
|
|
146
|
+
"""
|
|
147
|
+
patronURL = f"{self.url}/{identifier}"
|
|
148
|
+
response = requests.get(patronURL, headers=self.headers)
|
|
149
|
+
data = response.json()
|
|
150
|
+
if response.status_code != 200:
|
|
151
|
+
return {"status": "error", "code": response.status_code, "body": data}
|
|
152
|
+
return data
|
|
153
|
+
|
|
154
|
+
def create_patron(self, **data) -> dict | bool:
|
|
155
|
+
"""
|
|
156
|
+
Create a new patron in the account.
|
|
157
|
+
|
|
158
|
+
:param self: `Self@Libib`
|
|
159
|
+
:param **data: Patron fields as keyword arguments. Accepts any of:
|
|
160
|
+
barcode, first_name, last_name, email, notification_emails, tags, patron_id, phone, address1, address2, city, state, country, zip, freeze, password.
|
|
161
|
+
:return success:
|
|
162
|
+
```python
|
|
163
|
+
True
|
|
164
|
+
```
|
|
165
|
+
:return error:
|
|
166
|
+
```python
|
|
167
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
168
|
+
```
|
|
169
|
+
"""
|
|
170
|
+
valid_data = {k: v for k, v in data.items() if k in self.patron_sig}
|
|
171
|
+
invalid_data = {k: v for k, v in data.items() if k not in self.patron_sig}
|
|
172
|
+
if len(valid_data) == 0:
|
|
173
|
+
if len(invalid_data) > 0:
|
|
174
|
+
print("Data is invalid:", invalid_data)
|
|
175
|
+
return {"status": "error", "code": 400, "body": f"Data is invalid: {invalid_data}"}
|
|
176
|
+
else:
|
|
177
|
+
return {"status": "error", "code": 400, "body": "No Data Provided"}
|
|
178
|
+
params = valid_data
|
|
179
|
+
response = requests.post(self.url, headers=self.headers, params=params)
|
|
180
|
+
resp_data = response.json()
|
|
181
|
+
if response.status_code != 200:
|
|
182
|
+
return {"status": "error", "code": response.status_code, "body": resp_data}
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
def update_patron(self, identifier: str, **data) -> bool | dict:
|
|
186
|
+
"""
|
|
187
|
+
Update specific fields for an existing patron. Pass the patron's barcode or email as the identifier.
|
|
188
|
+
|
|
189
|
+
:param self: `Self@Libib`
|
|
190
|
+
:param identifier: Patron email/barcode
|
|
191
|
+
:type identifier: str
|
|
192
|
+
:param **data: Patron fields as keyword arguments. Accepts any of:
|
|
193
|
+
barcode, first_name, last_name, email, notification_emails, tags, patron_id, phone, address1, address2, city, state, country, zip, freeze, password.
|
|
194
|
+
:return success:
|
|
195
|
+
```python
|
|
196
|
+
True
|
|
197
|
+
```
|
|
198
|
+
:return error:
|
|
199
|
+
```python
|
|
200
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
201
|
+
```
|
|
202
|
+
"""
|
|
203
|
+
patronURL = f"{self.url}/{identifier}"
|
|
204
|
+
valid_data = {k: v for k, v in data.items() if k in self.patron_sig}
|
|
205
|
+
invalid_data = {k: v for k, v in data.items() if k not in self.patron_sig}
|
|
206
|
+
if len(valid_data) == 0:
|
|
207
|
+
if len(invalid_data) > 0:
|
|
208
|
+
print("Data is invalid:", invalid_data)
|
|
209
|
+
return {"status": "error", "code": 400, "body": f"Data is invalid: {invalid_data}"}
|
|
210
|
+
else:
|
|
211
|
+
return {"status": "error", "code": 400, "body": "No Data Provided"}
|
|
212
|
+
params = valid_data
|
|
213
|
+
response = requests.post(patronURL, headers=self.headers, params=params)
|
|
214
|
+
resp_data = response.json()
|
|
215
|
+
if response.status_code != 200:
|
|
216
|
+
return {"status": "error", "code": response.status_code, "body": resp_data}
|
|
217
|
+
return True
|
|
218
|
+
|
|
219
|
+
def restore_patron(self, identifier: str) -> bool | dict:
|
|
220
|
+
"""
|
|
221
|
+
Restore a previously deleted patron. Patrons can be restored within 30 days of deletion. Pass the patron's barcode or email as the identifier.
|
|
222
|
+
|
|
223
|
+
:param self: `Self@Libib`
|
|
224
|
+
:param identifier: Patron email/barcode
|
|
225
|
+
:type identifier: str
|
|
226
|
+
:return success:
|
|
227
|
+
```python
|
|
228
|
+
True
|
|
229
|
+
```
|
|
230
|
+
:return error:
|
|
231
|
+
```python
|
|
232
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
233
|
+
```
|
|
234
|
+
"""
|
|
235
|
+
patronURL = f"{self.url}/{identifier}"
|
|
236
|
+
response = requests.patch(patronURL, headers=self.headers)
|
|
237
|
+
resp_data = response.json()
|
|
238
|
+
if response.status_code != 200:
|
|
239
|
+
return {"status": "error", "code": response.status_code, "body": resp_data}
|
|
240
|
+
return True
|
|
241
|
+
|
|
242
|
+
def delete_patron(self, identifier: str) -> bool | dict:
|
|
243
|
+
"""
|
|
244
|
+
Remove a single patron by passing the patron's barcode or email as the identifier. Deleting a patron dissociates their entire lending/hold history.
|
|
245
|
+
|
|
246
|
+
:param self: `Self@Libib`
|
|
247
|
+
:param identifier: Patron email/barcode
|
|
248
|
+
:type identifier: str
|
|
249
|
+
:return success:
|
|
250
|
+
```python
|
|
251
|
+
True
|
|
252
|
+
```
|
|
253
|
+
:return error:
|
|
254
|
+
```python
|
|
255
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
256
|
+
```
|
|
257
|
+
"""
|
|
258
|
+
patronURL = f"{self.url}/{identifier}"
|
|
259
|
+
response = requests.delete(patronURL, headers=self.headers)
|
|
260
|
+
resp_data = response.json()
|
|
261
|
+
if response.status_code != 204:
|
|
262
|
+
return {"status": "error", "code": response.status_code, "body": resp_data}
|
|
263
|
+
return True
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class Managers:
|
|
267
|
+
"""
|
|
268
|
+
Manager subclient
|
|
269
|
+
"""
|
|
270
|
+
url = "https://api.libib.com/managers"
|
|
271
|
+
roles = {"admin", "manager", "lender"}
|
|
272
|
+
|
|
273
|
+
def __init__(self, client: Libib) -> None:
|
|
274
|
+
self.client = client
|
|
275
|
+
self.headers = client.apiHeaders
|
|
276
|
+
|
|
277
|
+
def get_managers(self) -> dict | list:
|
|
278
|
+
"""
|
|
279
|
+
Retrieve a list of all existing managers on your site – including the owner.
|
|
280
|
+
|
|
281
|
+
:param self: `Self@Libib`
|
|
282
|
+
|
|
283
|
+
:return success:
|
|
284
|
+
```python
|
|
285
|
+
[
|
|
286
|
+
{
|
|
287
|
+
"first_name": "Larry",
|
|
288
|
+
"last_name": "McMurtry",
|
|
289
|
+
"email": "lonesome@example.com",
|
|
290
|
+
"role": "owner",
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"first_name": "Samwise",
|
|
294
|
+
"last_name": "Gamgee",
|
|
295
|
+
"email": "samthewise@example.com",
|
|
296
|
+
"role": "lender",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"first_name": "Anaïs",
|
|
300
|
+
"last_name": "Nin",
|
|
301
|
+
"email": "angela@example.com",
|
|
302
|
+
"role": "admin",
|
|
303
|
+
},
|
|
304
|
+
]
|
|
305
|
+
```
|
|
306
|
+
:return error:
|
|
307
|
+
```python
|
|
308
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
309
|
+
```
|
|
310
|
+
"""
|
|
311
|
+
response = requests.get(self.url, headers=self.headers)
|
|
312
|
+
data = dict(response.json())
|
|
313
|
+
if response.status_code != 200:
|
|
314
|
+
return {"status": "error", "code": response.status_code, "body": data}
|
|
315
|
+
return data.get("managers", [])
|
|
316
|
+
|
|
317
|
+
def get_manager_by_id(self, email: str) -> dict:
|
|
318
|
+
"""
|
|
319
|
+
Retrieve a single manager by passing the manager's email address as an identifier.
|
|
320
|
+
|
|
321
|
+
:param self: `Self@Libib`
|
|
322
|
+
:param email: Manager email
|
|
323
|
+
:type email: str
|
|
324
|
+
:return success:
|
|
325
|
+
```python
|
|
326
|
+
{
|
|
327
|
+
"first_name": "Samwise",
|
|
328
|
+
"last_name": "Gamgee",
|
|
329
|
+
"email": "samthewise@example.com",
|
|
330
|
+
"role": "admin",
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
:return error:
|
|
334
|
+
```python
|
|
335
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
336
|
+
```
|
|
337
|
+
"""
|
|
338
|
+
managerURL = f"{self.url}/{email}"
|
|
339
|
+
response = requests.get(managerURL, headers=self.headers)
|
|
340
|
+
data = response.json()
|
|
341
|
+
if response.status_code != 200:
|
|
342
|
+
return {"status": "error", "code": response.status_code, "body": data}
|
|
343
|
+
return data
|
|
344
|
+
|
|
345
|
+
def create_manager(self, first_name: str, last_name: str, email: str, password: str, role: str) -> dict | bool:
|
|
346
|
+
"""
|
|
347
|
+
Create a new manager in the account. Must have open manager seats for this method to succeed.
|
|
348
|
+
|
|
349
|
+
> Assigning Collections:
|
|
350
|
+
> If user has a manager or lender role, assigning collections must be completed by the account owner using the website.
|
|
351
|
+
|
|
352
|
+
:param self: `Self@Libib`
|
|
353
|
+
:param first_name: Manager First Name
|
|
354
|
+
:type first_name: str
|
|
355
|
+
:param last_name: Manager Last Name
|
|
356
|
+
:type last_name: str
|
|
357
|
+
:param email: Manager Email
|
|
358
|
+
:type email: str
|
|
359
|
+
:param password: Manager Password
|
|
360
|
+
:type password: str
|
|
361
|
+
:param role: Manager Role
|
|
362
|
+
:type role: str
|
|
363
|
+
:rtype: dict[Any, Any]
|
|
364
|
+
:return success:
|
|
365
|
+
```python
|
|
366
|
+
True
|
|
367
|
+
```
|
|
368
|
+
:return error:
|
|
369
|
+
```python
|
|
370
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
371
|
+
```
|
|
372
|
+
"""
|
|
373
|
+
if role not in self.roles:
|
|
374
|
+
return {"status": "error", "code": 400, "body": f"Role is invalid: {role}"}
|
|
375
|
+
|
|
376
|
+
params = {
|
|
377
|
+
"first_name": first_name,
|
|
378
|
+
"last_name": last_name,
|
|
379
|
+
"email": email,
|
|
380
|
+
"password": password,
|
|
381
|
+
"role": role,
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
response = requests.post(url=self.url, headers=self.headers, params=params)
|
|
385
|
+
data = response.json()
|
|
386
|
+
|
|
387
|
+
if response.status_code != 200:
|
|
388
|
+
return {"status": "error", "code": response.status_code, "body": data}
|
|
389
|
+
|
|
390
|
+
return True
|
|
391
|
+
|
|
392
|
+
def delete_manager(self, email: str) -> bool | dict:
|
|
393
|
+
"""
|
|
394
|
+
Remove a single manager by their email address. You cannot remove the owner account via this method. Owners must delete their entire account manually.
|
|
395
|
+
|
|
396
|
+
:param self: `Self@Libib`
|
|
397
|
+
:param email: Manager email
|
|
398
|
+
:type email: str
|
|
399
|
+
:return success:
|
|
400
|
+
```python
|
|
401
|
+
True
|
|
402
|
+
```
|
|
403
|
+
:return error:
|
|
404
|
+
```python
|
|
405
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
406
|
+
```
|
|
407
|
+
"""
|
|
408
|
+
managerURL = f"{self.url}/{email}"
|
|
409
|
+
response = requests.delete(managerURL, headers=self.headers)
|
|
410
|
+
resp_data = response.json()
|
|
411
|
+
if response.status_code != 204:
|
|
412
|
+
return {"status": "error", "code": response.status_code, "body": resp_data}
|
|
413
|
+
return True
|
|
414
|
+
|
|
415
|
+
class Accounts:
|
|
416
|
+
url = "https://api.libib.com/accounts"
|
|
417
|
+
|
|
418
|
+
def __init__(self, client: Libib) -> None:
|
|
419
|
+
self.client = client
|
|
420
|
+
self.headers = client.apiHeaders
|
|
421
|
+
|
|
422
|
+
def get_accounts(self) -> dict | list:
|
|
423
|
+
"""
|
|
424
|
+
Retrieve a list of account information. For Pro users this will only return one account. For multi-account "Ultimate" users, this will return all accounts in their purview.
|
|
425
|
+
|
|
426
|
+
The API returns an object with an 'accounts' list; this method returns that inner list.
|
|
427
|
+
|
|
428
|
+
:param self: Self@Accounts
|
|
429
|
+
|
|
430
|
+
:return success:
|
|
431
|
+
```python
|
|
432
|
+
[
|
|
433
|
+
{
|
|
434
|
+
"organization": "My Organization Name",
|
|
435
|
+
"api_id": "q44f0eb130194c31e9081bcc2412a7fe0a5b47ab",
|
|
436
|
+
"email": "sriddell@example.com",
|
|
437
|
+
"first_name": "Sepideh",
|
|
438
|
+
"last_name": "Riddell",
|
|
439
|
+
"manager_seats": 10,
|
|
440
|
+
"url": "sr-library",
|
|
441
|
+
"authorized": 1,
|
|
442
|
+
}
|
|
443
|
+
]
|
|
444
|
+
```
|
|
445
|
+
:return error:
|
|
446
|
+
```python
|
|
447
|
+
{"status": "error", "code": response.status_code, "body": response.json()}
|
|
448
|
+
```
|
|
449
|
+
"""
|
|
450
|
+
response = requests.get(self.url, headers=self.headers)
|
|
451
|
+
data = dict(response.json())
|
|
452
|
+
if response.status_code != 200:
|
|
453
|
+
return {"status": "error", "code": response.status_code, "body": data}
|
|
454
|
+
return data.get("accounts", [])
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: libib-client
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A synchronous, third-party client for the Libib API
|
|
5
|
+
Author: Michael Masarik
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://michael-masarik.github.io/libib-client/
|
|
8
|
+
Project-URL: Repository, https://github.com/michael-masarik/libib-client
|
|
9
|
+
Project-URL: Issues, https://github.com/michael-masarik/libib-client/issues
|
|
10
|
+
Keywords: libib,library,api,client
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: requests>=2.31.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# Libib-Client
|
|
18
|
+
A synchronous, third-party client for the [Libib API](https://support.libib.com/rest-api/introduction.html)
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
```shell
|
|
22
|
+
pip install libib-client
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
To initalize the client:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import Libib
|
|
31
|
+
|
|
32
|
+
# For Pro accounts
|
|
33
|
+
client = Libib("your-api-key", "your-user-id")
|
|
34
|
+
|
|
35
|
+
# For Ultimate accounts, also pass your Ultimate ID
|
|
36
|
+
client = Libib("your-api-key", "your-user-id", "your-ultimate-id")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Documentation:
|
|
40
|
+
Documentaion can be [found here] (https://michael-masarik.github.io/libib-client/)
|
|
41
|
+
|
|
42
|
+
## Note
|
|
43
|
+
|
|
44
|
+
I do not have an Ultimate account, so if the Ultimate features (or any features, for that matter) do not work, feel free to open an issue or a PR
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/libib-client/__init__.py
|
|
5
|
+
src/libib-client/main.py
|
|
6
|
+
src/libib_client.egg-info/PKG-INFO
|
|
7
|
+
src/libib_client.egg-info/SOURCES.txt
|
|
8
|
+
src/libib_client.egg-info/dependency_links.txt
|
|
9
|
+
src/libib_client.egg-info/requires.txt
|
|
10
|
+
src/libib_client.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.31.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
libib-client
|