personal_knowledge_library 3.0.0__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.
Potentially problematic release.
This version of personal_knowledge_library might be problematic. Click here for more details.
- knowledge/__init__.py +91 -0
- knowledge/base/__init__.py +22 -0
- knowledge/base/access.py +167 -0
- knowledge/base/entity.py +267 -0
- knowledge/base/language.py +27 -0
- knowledge/base/ontology.py +2734 -0
- knowledge/base/search.py +473 -0
- knowledge/base/tenant.py +192 -0
- knowledge/nel/__init__.py +11 -0
- knowledge/nel/base.py +495 -0
- knowledge/nel/engine.py +123 -0
- knowledge/ontomapping/__init__.py +667 -0
- knowledge/ontomapping/manager.py +320 -0
- knowledge/public/__init__.py +27 -0
- knowledge/public/cache.py +115 -0
- knowledge/public/helper.py +373 -0
- knowledge/public/relations.py +128 -0
- knowledge/public/wikidata.py +1324 -0
- knowledge/services/__init__.py +128 -0
- knowledge/services/asyncio/__init__.py +7 -0
- knowledge/services/asyncio/base.py +458 -0
- knowledge/services/asyncio/graph.py +1420 -0
- knowledge/services/asyncio/group.py +450 -0
- knowledge/services/asyncio/search.py +439 -0
- knowledge/services/asyncio/users.py +270 -0
- knowledge/services/base.py +533 -0
- knowledge/services/graph.py +1897 -0
- knowledge/services/group.py +819 -0
- knowledge/services/helper.py +142 -0
- knowledge/services/ontology.py +1234 -0
- knowledge/services/search.py +488 -0
- knowledge/services/session.py +444 -0
- knowledge/services/tenant.py +281 -0
- knowledge/services/users.py +445 -0
- knowledge/utils/__init__.py +10 -0
- knowledge/utils/graph.py +417 -0
- knowledge/utils/wikidata.py +197 -0
- knowledge/utils/wikipedia.py +175 -0
- personal_knowledge_library-3.0.0.dist-info/LICENSE +201 -0
- personal_knowledge_library-3.0.0.dist-info/METADATA +1163 -0
- personal_knowledge_library-3.0.0.dist-info/RECORD +42 -0
- personal_knowledge_library-3.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright © 2024 Wacom. All rights reserved.
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Dict, Any, Optional, List
|
|
5
|
+
|
|
6
|
+
import orjson
|
|
7
|
+
|
|
8
|
+
from knowledge.base.language import LocaleCode
|
|
9
|
+
from knowledge.base.search import DocumentSearchResponse, LabelMatchingResponse, VectorDBDocument
|
|
10
|
+
from knowledge.services import (
|
|
11
|
+
DEFAULT_TIMEOUT,
|
|
12
|
+
AUTHORIZATION_HEADER_FLAG,
|
|
13
|
+
APPLICATION_JSON_HEADER,
|
|
14
|
+
CONTENT_TYPE_HEADER_FLAG,
|
|
15
|
+
USER_AGENT_HEADER_FLAG,
|
|
16
|
+
)
|
|
17
|
+
from knowledge.services.asyncio.base import AsyncServiceAPIClient, handle_error
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AsyncSemanticSearchClient(AsyncServiceAPIClient):
|
|
21
|
+
"""
|
|
22
|
+
Semantic Search Client
|
|
23
|
+
======================
|
|
24
|
+
Client for searching semantically similar documents and labels.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
service_url: str
|
|
29
|
+
Service URL for the client.
|
|
30
|
+
service_endpoint: str (Default:= 'vector/v1')
|
|
31
|
+
Service endpoint for the client.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, service_url: str, service_endpoint: str = "vector/api/v1"):
|
|
35
|
+
super().__init__("Async Semantic Search ", service_url, service_endpoint)
|
|
36
|
+
|
|
37
|
+
async def retrieve_document_chunks(
|
|
38
|
+
self, locale: LocaleCode, uri: str, auth_key: Optional[str] = None
|
|
39
|
+
) -> List[VectorDBDocument]:
|
|
40
|
+
"""
|
|
41
|
+
Retrieve document chunks from vector database. The service is automatically chunking the document into
|
|
42
|
+
smaller parts. The chunks are returned as a list of dictionaries, with metadata and content.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
locale: LocaleCode
|
|
47
|
+
Locale
|
|
48
|
+
uri: str
|
|
49
|
+
URI of the document
|
|
50
|
+
auth_key: Optional[str] (Default:= None)
|
|
51
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
document: Dict[str, Any]
|
|
56
|
+
List of document chunks with metadata and content related to the document.
|
|
57
|
+
|
|
58
|
+
Raises
|
|
59
|
+
------
|
|
60
|
+
WacomServiceException
|
|
61
|
+
If the request fails.
|
|
62
|
+
"""
|
|
63
|
+
if auth_key is None:
|
|
64
|
+
auth_key, _ = await self.handle_token()
|
|
65
|
+
url: str = f"{self.service_base_url}documents/"
|
|
66
|
+
|
|
67
|
+
headers: Dict[str, str] = {
|
|
68
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
69
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
70
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
71
|
+
}
|
|
72
|
+
async with self.__async_session__() as session:
|
|
73
|
+
async with session.get(
|
|
74
|
+
url, params={"locale": locale, "uri": uri}, headers=headers, timeout=DEFAULT_TIMEOUT
|
|
75
|
+
) as response:
|
|
76
|
+
if response.ok:
|
|
77
|
+
docs: List[VectorDBDocument] = [
|
|
78
|
+
VectorDBDocument(vec_doc) for vec_doc in await response.json(loads=orjson.loads)
|
|
79
|
+
]
|
|
80
|
+
else:
|
|
81
|
+
raise await handle_error(
|
|
82
|
+
"Failed to retrieve the document.",
|
|
83
|
+
response,
|
|
84
|
+
headers=headers,
|
|
85
|
+
parameters={"locale": locale, "uri": uri},
|
|
86
|
+
)
|
|
87
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
88
|
+
return docs
|
|
89
|
+
|
|
90
|
+
async def retrieve_labels(
|
|
91
|
+
self, locale: LocaleCode, uri: str, auth_key: Optional[str] = None
|
|
92
|
+
) -> List[VectorDBDocument]:
|
|
93
|
+
"""
|
|
94
|
+
Retrieve labels from vector database.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
locale: LocaleCode
|
|
99
|
+
Locale
|
|
100
|
+
uri: str
|
|
101
|
+
URI of the document
|
|
102
|
+
auth_key: Optional[str] (Default:= None)
|
|
103
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
document: List[VectorDBDocument]
|
|
108
|
+
List of labels with metadata and content related to the entity with uri.
|
|
109
|
+
|
|
110
|
+
Raises
|
|
111
|
+
------
|
|
112
|
+
WacomServiceException
|
|
113
|
+
If the request fails.
|
|
114
|
+
"""
|
|
115
|
+
if auth_key is None:
|
|
116
|
+
auth_key, _ = await self.handle_token()
|
|
117
|
+
url: str = f"{self.service_base_url}labels/"
|
|
118
|
+
|
|
119
|
+
headers: Dict[str, str] = {
|
|
120
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
121
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
122
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
123
|
+
}
|
|
124
|
+
async with self.__async_session__() as session:
|
|
125
|
+
async with session.get(
|
|
126
|
+
url, params={"locale": locale, "uri": uri}, headers=headers, timeout=DEFAULT_TIMEOUT
|
|
127
|
+
) as response:
|
|
128
|
+
if response.ok:
|
|
129
|
+
docs: List[VectorDBDocument] = [
|
|
130
|
+
VectorDBDocument(vec_doc) for vec_doc in await response.json(loads=orjson.loads)
|
|
131
|
+
]
|
|
132
|
+
else:
|
|
133
|
+
raise await handle_error(
|
|
134
|
+
"Failed to retrieve the document.",
|
|
135
|
+
response,
|
|
136
|
+
headers=headers,
|
|
137
|
+
parameters={"locale": locale, "uri": uri},
|
|
138
|
+
)
|
|
139
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
140
|
+
return docs
|
|
141
|
+
|
|
142
|
+
async def count_documents(
|
|
143
|
+
self, locale: LocaleCode, concept_type: Optional[str] = None, auth_key: Optional[str] = None
|
|
144
|
+
) -> int:
|
|
145
|
+
"""
|
|
146
|
+
Count all documents for a tenant.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
locale: str
|
|
151
|
+
Locale
|
|
152
|
+
concept_type: Optional[str] (Default:= None)
|
|
153
|
+
Concept type.
|
|
154
|
+
auth_key: Optional[str] (Default:= None)
|
|
155
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
number_of_docs: int
|
|
160
|
+
Number of documents.
|
|
161
|
+
|
|
162
|
+
Raises
|
|
163
|
+
------
|
|
164
|
+
WacomServiceException
|
|
165
|
+
If the request fails.
|
|
166
|
+
"""
|
|
167
|
+
if auth_key is None:
|
|
168
|
+
auth_key, _ = await self.handle_token()
|
|
169
|
+
url: str = f"{self.service_base_url}documents/count/"
|
|
170
|
+
headers: Dict[str, str] = {
|
|
171
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
172
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
173
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
174
|
+
}
|
|
175
|
+
params: Dict[str, Any] = {"locale": locale}
|
|
176
|
+
if concept_type:
|
|
177
|
+
params["concept_type"] = concept_type
|
|
178
|
+
async with self.__async_session__() as session:
|
|
179
|
+
async with session.get(url, params=params, headers=headers) as response:
|
|
180
|
+
if response.ok:
|
|
181
|
+
count: int = (await response.json(loads=orjson.loads)).get("count", 0)
|
|
182
|
+
else:
|
|
183
|
+
raise await handle_error(
|
|
184
|
+
"Counting documents failed.", response, headers=headers, parameters={"locale": locale}
|
|
185
|
+
)
|
|
186
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
187
|
+
return count
|
|
188
|
+
|
|
189
|
+
async def count_documents_filter(
|
|
190
|
+
self, locale: LocaleCode, filters: Dict[str, Any], auth_key: Optional[str] = None
|
|
191
|
+
) -> int:
|
|
192
|
+
"""
|
|
193
|
+
Count all documents for a tenant using a filter.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
locale: str
|
|
198
|
+
Locale
|
|
199
|
+
filters: Dict[str, Any]
|
|
200
|
+
Filters for the search
|
|
201
|
+
auth_key: Optional[str] (Default:= None)
|
|
202
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
number_of_docs: int
|
|
207
|
+
Number of documents.
|
|
208
|
+
|
|
209
|
+
Raises
|
|
210
|
+
------
|
|
211
|
+
WacomServiceException
|
|
212
|
+
If the request fails.
|
|
213
|
+
"""
|
|
214
|
+
if auth_key is None:
|
|
215
|
+
auth_key, _ = await self.handle_token()
|
|
216
|
+
url: str = f"{self.service_base_url}documents/count/filter"
|
|
217
|
+
headers: Dict[str, str] = {
|
|
218
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
219
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
220
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
221
|
+
}
|
|
222
|
+
async with self.__async_session__() as session:
|
|
223
|
+
async with session.post(url, json={"locale": locale, "filter": filters}, headers=headers) as response:
|
|
224
|
+
if response.ok:
|
|
225
|
+
count: int = (await response.json(loads=orjson.loads)).get("count", 0)
|
|
226
|
+
else:
|
|
227
|
+
raise await handle_error(
|
|
228
|
+
"Counting documents failed.",
|
|
229
|
+
response,
|
|
230
|
+
headers=headers,
|
|
231
|
+
parameters={"locale": locale, "filter": filters},
|
|
232
|
+
)
|
|
233
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
234
|
+
return count
|
|
235
|
+
|
|
236
|
+
async def count_labels(
|
|
237
|
+
self, locale: str, concept_type: Optional[str] = None, auth_key: Optional[str] = None
|
|
238
|
+
) -> int:
|
|
239
|
+
"""
|
|
240
|
+
Count all labels entries for a tenant.
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
locale: str
|
|
245
|
+
Locale
|
|
246
|
+
concept_type: Optional[str] (Default:= None)
|
|
247
|
+
Concept type.
|
|
248
|
+
auth_key: Optional[str] (Default:= None)
|
|
249
|
+
If auth key is provided, it will be used for the request.
|
|
250
|
+
|
|
251
|
+
Returns
|
|
252
|
+
-------
|
|
253
|
+
count: int
|
|
254
|
+
Number of words.
|
|
255
|
+
|
|
256
|
+
Raises
|
|
257
|
+
------
|
|
258
|
+
WacomServiceException
|
|
259
|
+
If the request fails.
|
|
260
|
+
"""
|
|
261
|
+
if auth_key is None:
|
|
262
|
+
auth_key, _ = await self.handle_token()
|
|
263
|
+
url: str = f"{self.service_base_url}labels/count/"
|
|
264
|
+
headers: Dict[str, str] = {
|
|
265
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
266
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
267
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
268
|
+
}
|
|
269
|
+
params: Dict[str, Any] = {"locale": locale}
|
|
270
|
+
if concept_type:
|
|
271
|
+
params["concept_type"] = concept_type
|
|
272
|
+
async with self.__async_session__() as session:
|
|
273
|
+
async with session.get(url, params=params, headers=headers) as response:
|
|
274
|
+
if response.ok:
|
|
275
|
+
count: int = (await response.json(loads=orjson.loads)).get("count", 0)
|
|
276
|
+
else:
|
|
277
|
+
raise await handle_error(
|
|
278
|
+
"Counting labels failed.", response, headers=headers, parameters={"locale": locale}
|
|
279
|
+
)
|
|
280
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
281
|
+
return count
|
|
282
|
+
|
|
283
|
+
async def count_labels_filter(
|
|
284
|
+
self, locale: LocaleCode, filters: Dict[str, Any], auth_key: Optional[str] = None
|
|
285
|
+
) -> int:
|
|
286
|
+
"""
|
|
287
|
+
Count all labels for a tenant using a filter.
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
locale: str
|
|
292
|
+
Locale
|
|
293
|
+
filters: Dict[str, Any]
|
|
294
|
+
Filters for the search
|
|
295
|
+
auth_key: Optional[str] (Default:= None)
|
|
296
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
297
|
+
|
|
298
|
+
Returns
|
|
299
|
+
-------
|
|
300
|
+
number_of_docs: int
|
|
301
|
+
Number of documents.
|
|
302
|
+
|
|
303
|
+
Raises
|
|
304
|
+
------
|
|
305
|
+
WacomServiceException
|
|
306
|
+
If the request fails.
|
|
307
|
+
"""
|
|
308
|
+
if auth_key is None:
|
|
309
|
+
auth_key, _ = await self.handle_token()
|
|
310
|
+
url: str = f"{self.service_base_url}labels/count/filter"
|
|
311
|
+
headers: Dict[str, str] = {
|
|
312
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
313
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
314
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
315
|
+
}
|
|
316
|
+
async with self.__async_session__() as session:
|
|
317
|
+
async with session.post(url, json={"locale": locale, "filter": filters}, headers=headers) as response:
|
|
318
|
+
if response.ok:
|
|
319
|
+
count: int = (await response.json(loads=orjson.loads)).get("count", 0)
|
|
320
|
+
else:
|
|
321
|
+
raise await handle_error(
|
|
322
|
+
"Counting documents failed.",
|
|
323
|
+
response,
|
|
324
|
+
headers=headers,
|
|
325
|
+
parameters={"locale": locale, "filter": filters},
|
|
326
|
+
)
|
|
327
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
328
|
+
return count
|
|
329
|
+
|
|
330
|
+
async def document_search(
|
|
331
|
+
self,
|
|
332
|
+
query: str,
|
|
333
|
+
locale: str,
|
|
334
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
335
|
+
max_results: int = 10,
|
|
336
|
+
auth_key: Optional[str] = None,
|
|
337
|
+
) -> DocumentSearchResponse:
|
|
338
|
+
"""
|
|
339
|
+
Async Semantic search.
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
----------
|
|
343
|
+
query: str
|
|
344
|
+
Query text for the search
|
|
345
|
+
locale: str
|
|
346
|
+
Locale of the text
|
|
347
|
+
filters: Optional[Dict[str, Any]] = None
|
|
348
|
+
Filters for the search
|
|
349
|
+
max_results: int
|
|
350
|
+
Maximum number of results
|
|
351
|
+
auth_key: Optional[str] (Default:= None)
|
|
352
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
353
|
+
|
|
354
|
+
Returns
|
|
355
|
+
-------
|
|
356
|
+
response: DocumentSearchResponse
|
|
357
|
+
Search results response.
|
|
358
|
+
|
|
359
|
+
Raises
|
|
360
|
+
------
|
|
361
|
+
WacomServiceException
|
|
362
|
+
If the request fails.
|
|
363
|
+
"""
|
|
364
|
+
if auth_key is None:
|
|
365
|
+
auth_key, _ = await self.handle_token()
|
|
366
|
+
url: str = f"{self.service_base_url}documents/search/"
|
|
367
|
+
headers: Dict[str, str] = {
|
|
368
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
369
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
370
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
371
|
+
}
|
|
372
|
+
params: Dict[str, Any] = {
|
|
373
|
+
"query": query,
|
|
374
|
+
"metadata": filters if filters else {},
|
|
375
|
+
"locale": locale,
|
|
376
|
+
"max_results": max_results,
|
|
377
|
+
}
|
|
378
|
+
async with self.__async_session__() as session:
|
|
379
|
+
async with session.post(url, headers=headers, json=params) as response:
|
|
380
|
+
if response.ok:
|
|
381
|
+
response_dict: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
382
|
+
else:
|
|
383
|
+
raise await handle_error("Semantic Search failed.", response, headers=headers, parameters=params)
|
|
384
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
385
|
+
return DocumentSearchResponse.from_dict(response_dict)
|
|
386
|
+
|
|
387
|
+
async def labels_search(
|
|
388
|
+
self,
|
|
389
|
+
query: str,
|
|
390
|
+
locale: str,
|
|
391
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
392
|
+
max_results: int = 10,
|
|
393
|
+
auth_key: Optional[str] = None,
|
|
394
|
+
) -> LabelMatchingResponse:
|
|
395
|
+
"""
|
|
396
|
+
Async search for semantically similar labels.
|
|
397
|
+
|
|
398
|
+
Parameters
|
|
399
|
+
----------
|
|
400
|
+
query: str
|
|
401
|
+
Query text for the search
|
|
402
|
+
locale: str
|
|
403
|
+
Locale of the text
|
|
404
|
+
filters: Optional[Dict[str, Any]] = None
|
|
405
|
+
Filters for the search
|
|
406
|
+
max_results: int
|
|
407
|
+
Maximum number of results
|
|
408
|
+
auth_key: Optional[str] (Default:= None)
|
|
409
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
410
|
+
|
|
411
|
+
Returns
|
|
412
|
+
-------
|
|
413
|
+
response: LabelMatchingResponse
|
|
414
|
+
Search results response.
|
|
415
|
+
"""
|
|
416
|
+
if auth_key is None:
|
|
417
|
+
auth_key, _ = await self.handle_token()
|
|
418
|
+
url: str = f"{self.service_base_url}labels/match/"
|
|
419
|
+
headers: Dict[str, str] = {
|
|
420
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
421
|
+
CONTENT_TYPE_HEADER_FLAG: APPLICATION_JSON_HEADER,
|
|
422
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
423
|
+
}
|
|
424
|
+
params: Dict[str, Any] = {
|
|
425
|
+
"query": query,
|
|
426
|
+
"metadata": filters if filters else {},
|
|
427
|
+
"locale": locale,
|
|
428
|
+
"max_results": max_results,
|
|
429
|
+
}
|
|
430
|
+
async with self.__async_session__() as session:
|
|
431
|
+
async with session.post(url, headers=headers, json=params) as response:
|
|
432
|
+
if response.ok:
|
|
433
|
+
response_dict: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
434
|
+
else:
|
|
435
|
+
raise await handle_error(
|
|
436
|
+
"Label fuzzy matching failed.", response, headers=headers, parameters=params
|
|
437
|
+
)
|
|
438
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
439
|
+
return LabelMatchingResponse.from_dict(response_dict)
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright © 2024 Wacom. All rights reserved.
|
|
3
|
+
import asyncio
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any, Union, Dict, List, Tuple
|
|
6
|
+
|
|
7
|
+
import orjson
|
|
8
|
+
|
|
9
|
+
from knowledge import logger
|
|
10
|
+
from knowledge.services import APPLICATION_JSON_HEADER, EXPIRATION_DATE_TAG
|
|
11
|
+
from knowledge.services.asyncio.base import AsyncServiceAPIClient, handle_error
|
|
12
|
+
from knowledge.services.base import WacomServiceAPIClient
|
|
13
|
+
from knowledge.services.users import (
|
|
14
|
+
UserRole,
|
|
15
|
+
USER_AGENT_TAG,
|
|
16
|
+
TENANT_API_KEY_FLAG,
|
|
17
|
+
OFFSET_TAG,
|
|
18
|
+
LIMIT_TAG,
|
|
19
|
+
User,
|
|
20
|
+
USER_ID_TAG,
|
|
21
|
+
EXTERNAL_USER_ID_TAG,
|
|
22
|
+
FORCE_TAG,
|
|
23
|
+
ROLES_TAG,
|
|
24
|
+
META_DATA_TAG,
|
|
25
|
+
CONTENT_TYPE_FLAG,
|
|
26
|
+
DEFAULT_TIMEOUT,
|
|
27
|
+
INTERNAL_USER_ID_TAG,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AsyncUserManagementService(AsyncServiceAPIClient):
|
|
32
|
+
"""
|
|
33
|
+
Async User-Management Service API
|
|
34
|
+
---------------------------------
|
|
35
|
+
Functionality:
|
|
36
|
+
- List all users
|
|
37
|
+
- Create / update / delete users
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
application_name: str
|
|
42
|
+
Name of the application
|
|
43
|
+
service_url: str
|
|
44
|
+
URL of the service
|
|
45
|
+
service_endpoint: str
|
|
46
|
+
Base endpoint
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
USER_DETAILS_ENDPOINT: str = f"{WacomServiceAPIClient.USER_ENDPOINT}/internal-id"
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
application_name: str,
|
|
54
|
+
service_url: str = WacomServiceAPIClient.SERVICE_URL,
|
|
55
|
+
service_endpoint: str = "graph/v1",
|
|
56
|
+
):
|
|
57
|
+
super().__init__(application_name=application_name, service_url=service_url, service_endpoint=service_endpoint)
|
|
58
|
+
|
|
59
|
+
# ------------------------------------------ Users handling --------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
async def create_user(
|
|
62
|
+
self, tenant_key: str, external_id: str, meta_data: Dict[str, str] = None, roles: List[UserRole] = None
|
|
63
|
+
) -> Tuple[User, str, str, datetime]:
|
|
64
|
+
"""
|
|
65
|
+
Creates user for a tenant.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
tenant_key: str -
|
|
70
|
+
API key for tenant
|
|
71
|
+
external_id: str -
|
|
72
|
+
External id of user identification service.
|
|
73
|
+
meta_data: Dict[str, str]
|
|
74
|
+
Meta-data dictionary.
|
|
75
|
+
roles: List[UserRole]
|
|
76
|
+
List of roles.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
user: User
|
|
81
|
+
Instance of the user
|
|
82
|
+
token: str
|
|
83
|
+
Auth token for user
|
|
84
|
+
refresh_key: str
|
|
85
|
+
Refresh token
|
|
86
|
+
expiration_time: datetime
|
|
87
|
+
Expiration time
|
|
88
|
+
Raises
|
|
89
|
+
------
|
|
90
|
+
WacomServiceException
|
|
91
|
+
If the tenant service returns an error code.
|
|
92
|
+
"""
|
|
93
|
+
url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
|
|
94
|
+
headers: dict = {
|
|
95
|
+
USER_AGENT_TAG: self.user_agent,
|
|
96
|
+
TENANT_API_KEY_FLAG: tenant_key,
|
|
97
|
+
CONTENT_TYPE_FLAG: "application/json",
|
|
98
|
+
}
|
|
99
|
+
payload: dict = {
|
|
100
|
+
EXTERNAL_USER_ID_TAG: external_id,
|
|
101
|
+
META_DATA_TAG: meta_data if meta_data is not None else {},
|
|
102
|
+
ROLES_TAG: [r.value for r in roles] if roles is not None else [UserRole.USER.value],
|
|
103
|
+
}
|
|
104
|
+
async with self.__async_session__() as session:
|
|
105
|
+
async with session.post(
|
|
106
|
+
url, headers=headers, json=payload, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
|
|
107
|
+
) as response:
|
|
108
|
+
if response.ok:
|
|
109
|
+
results: Dict[str, Union[str, Dict[str, str], List[str]]] = await response.json(loads=orjson.loads)
|
|
110
|
+
try:
|
|
111
|
+
date_object: datetime = datetime.fromisoformat(results["token"][EXPIRATION_DATE_TAG])
|
|
112
|
+
except (TypeError, ValueError) as _:
|
|
113
|
+
date_object: datetime = datetime.now()
|
|
114
|
+
logger.warning(f'Parsing of expiration date failed. {results["token"][EXPIRATION_DATE_TAG]}')
|
|
115
|
+
|
|
116
|
+
else:
|
|
117
|
+
raise await handle_error("Failed to create the user.", response, headers=headers, payload=payload)
|
|
118
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
119
|
+
return (
|
|
120
|
+
User.parse(results["user"]),
|
|
121
|
+
results["token"]["accessToken"],
|
|
122
|
+
results["token"]["refreshToken"],
|
|
123
|
+
date_object,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
async def update_user(
|
|
127
|
+
self,
|
|
128
|
+
tenant_key: str,
|
|
129
|
+
internal_id: str,
|
|
130
|
+
external_id: str,
|
|
131
|
+
meta_data: Dict[str, str] = None,
|
|
132
|
+
roles: List[UserRole] = None,
|
|
133
|
+
):
|
|
134
|
+
"""Updates user for a tenant.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
tenant_key: str
|
|
139
|
+
API key for tenant
|
|
140
|
+
internal_id: str
|
|
141
|
+
Internal id of semantic service.
|
|
142
|
+
external_id: str
|
|
143
|
+
External id of user identification service.
|
|
144
|
+
meta_data: Dict[str, str]
|
|
145
|
+
Meta-data dictionary.
|
|
146
|
+
roles: List[UserRole]
|
|
147
|
+
List of roles.
|
|
148
|
+
|
|
149
|
+
Raises
|
|
150
|
+
------
|
|
151
|
+
WacomServiceException
|
|
152
|
+
If the tenant service returns an error code.
|
|
153
|
+
"""
|
|
154
|
+
url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
|
|
155
|
+
headers: Dict[str, str] = {
|
|
156
|
+
USER_AGENT_TAG: self.user_agent,
|
|
157
|
+
TENANT_API_KEY_FLAG: tenant_key,
|
|
158
|
+
CONTENT_TYPE_FLAG: APPLICATION_JSON_HEADER,
|
|
159
|
+
}
|
|
160
|
+
payload: Dict[str, str] = {
|
|
161
|
+
META_DATA_TAG: meta_data if meta_data is not None else {},
|
|
162
|
+
ROLES_TAG: [r.value for r in roles] if roles is not None else [UserRole.USER.value],
|
|
163
|
+
}
|
|
164
|
+
params: Dict[str, str] = {USER_ID_TAG: internal_id, EXTERNAL_USER_ID_TAG: external_id}
|
|
165
|
+
async with self.__async_session__() as session:
|
|
166
|
+
async with session.patch(
|
|
167
|
+
url, headers=headers, json=payload, params=params, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
|
|
168
|
+
) as response:
|
|
169
|
+
if not response.ok:
|
|
170
|
+
raise await handle_error("Failed to update the user.", response, headers=headers, payload=payload)
|
|
171
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
172
|
+
|
|
173
|
+
async def delete_user(self, tenant_key: str, external_id: str, internal_id: str, force: bool = False):
|
|
174
|
+
"""Deletes user from tenant.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
tenant_key: str
|
|
179
|
+
API key for tenant
|
|
180
|
+
external_id: str
|
|
181
|
+
External id of user identification service.
|
|
182
|
+
internal_id: str
|
|
183
|
+
Internal id of user.
|
|
184
|
+
force: bool
|
|
185
|
+
If set to true removes all user data including groups and entities.
|
|
186
|
+
|
|
187
|
+
Raises
|
|
188
|
+
------
|
|
189
|
+
WacomServiceException
|
|
190
|
+
If the tenant service returns an error code.
|
|
191
|
+
"""
|
|
192
|
+
url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
|
|
193
|
+
headers: Dict[str, str] = {USER_AGENT_TAG: self.user_agent, TENANT_API_KEY_FLAG: tenant_key}
|
|
194
|
+
params: Dict[str, str] = {USER_ID_TAG: internal_id, EXTERNAL_USER_ID_TAG: external_id, FORCE_TAG: str(force)}
|
|
195
|
+
async with self.__async_session__() as session:
|
|
196
|
+
async with session.delete(
|
|
197
|
+
url, headers=headers, params=params, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
|
|
198
|
+
) as response:
|
|
199
|
+
if not response.ok:
|
|
200
|
+
raise await handle_error("Failed to delete the user.", response, headers=headers)
|
|
201
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
202
|
+
|
|
203
|
+
async def user_internal_id(self, tenant_key: str, external_id: str) -> str:
|
|
204
|
+
"""User internal id.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
tenant_key: str
|
|
209
|
+
API key for tenant
|
|
210
|
+
external_id: str
|
|
211
|
+
External id of user
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
internal_user_id: str
|
|
216
|
+
Internal id of users
|
|
217
|
+
|
|
218
|
+
Raises
|
|
219
|
+
------
|
|
220
|
+
WacomServiceException
|
|
221
|
+
If the tenant service returns an error code.
|
|
222
|
+
"""
|
|
223
|
+
url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_DETAILS_ENDPOINT}"
|
|
224
|
+
headers: dict = {USER_AGENT_TAG: self.user_agent, TENANT_API_KEY_FLAG: tenant_key}
|
|
225
|
+
parameters: Dict[str, str] = {EXTERNAL_USER_ID_TAG: external_id}
|
|
226
|
+
async with self.__async_session__() as session:
|
|
227
|
+
async with session.get(
|
|
228
|
+
url, headers=headers, params=parameters, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
|
|
229
|
+
) as response:
|
|
230
|
+
if response.ok:
|
|
231
|
+
response_dict: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
232
|
+
else:
|
|
233
|
+
raise await handle_error("Failed to get the user.", response, headers=headers)
|
|
234
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
235
|
+
return response_dict[INTERNAL_USER_ID_TAG]
|
|
236
|
+
|
|
237
|
+
async def listing_users(self, tenant_key: str, offset: int = 0, limit: int = 20) -> List[User]:
|
|
238
|
+
"""
|
|
239
|
+
Listing all users configured for this instance.
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
tenant_key: str
|
|
244
|
+
API key for tenant
|
|
245
|
+
offset: int - [optional]
|
|
246
|
+
Offset value to define starting position in list. [DEFAULT:= 0]
|
|
247
|
+
limit: int - [optional]
|
|
248
|
+
Define the limit of the list size. [DEFAULT:= 20]
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
user: List[User]
|
|
253
|
+
List of users.
|
|
254
|
+
"""
|
|
255
|
+
url: str = f"{self.service_base_url}{AsyncUserManagementService.USER_ENDPOINT}"
|
|
256
|
+
headers: Dict[str, str] = {USER_AGENT_TAG: self.user_agent, TENANT_API_KEY_FLAG: tenant_key}
|
|
257
|
+
params: Dict[str, str] = {OFFSET_TAG: offset, LIMIT_TAG: limit}
|
|
258
|
+
async with self.__async_session__() as session:
|
|
259
|
+
async with session.get(
|
|
260
|
+
url, headers=headers, params=params, timeout=DEFAULT_TIMEOUT, verify_ssl=self.verify_calls
|
|
261
|
+
) as response:
|
|
262
|
+
if response.ok:
|
|
263
|
+
users: List[Dict[str, Any]] = await response.json(loads=orjson.loads)
|
|
264
|
+
results: List[User] = []
|
|
265
|
+
for u in users:
|
|
266
|
+
results.append(User.parse(u))
|
|
267
|
+
else:
|
|
268
|
+
await handle_error("Listing of users failed.", response, headers=headers, parameters=params)
|
|
269
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
270
|
+
return results
|