personal_knowledge_library 3.2.1__tar.gz → 3.3.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.
Potentially problematic release.
This version of personal_knowledge_library might be problematic. Click here for more details.
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/PKG-INFO +4 -3
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/__init__.py +1 -1
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/ontology.py +1 -1
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/nel/base.py +19 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/nel/engine.py +2 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/asyncio/graph.py +278 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/base.py +6 -2
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/graph.py +124 -10
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/search.py +4 -1
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/pyproject.toml +1 -1
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/LICENSE +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/README.md +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/access.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/entity.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/language.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/response.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/search.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/tenant.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/nel/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/ontomapping/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/ontomapping/manager.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/cache.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/client.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/helper.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/relations.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/wikidata.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/asyncio/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/asyncio/base.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/asyncio/group.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/asyncio/search.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/asyncio/users.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/group.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/helper.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/ontology.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/session.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/tenant.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/users.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/__init__.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/diff.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/graph.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/import_format.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/wikidata.py +0 -0
- {personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/wikipedia.py +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: personal_knowledge_library
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Library to access Wacom's Personal Knowledge graph.
|
|
5
|
-
License: Apache-2.0
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: semantic-knowledge,knowledge-graph
|
|
7
8
|
Author: Markus Weber
|
|
8
9
|
Author-email: markus.weber@wacom.com
|
|
@@ -162,6 +162,10 @@ class KnowledgeGraphEntity(NamedEntity):
|
|
|
162
162
|
List of ontology types (class names)
|
|
163
163
|
entity_type: EntityType
|
|
164
164
|
Type of the entity.
|
|
165
|
+
tokens: Optional[List[str]] (default:=None)
|
|
166
|
+
List of tokens used to identify the entity.
|
|
167
|
+
token_indexes: Optional[List[int]] (default:=None)
|
|
168
|
+
List of token indexes used to identify the entity.
|
|
165
169
|
"""
|
|
166
170
|
|
|
167
171
|
def __init__(
|
|
@@ -175,6 +179,9 @@ class KnowledgeGraphEntity(NamedEntity):
|
|
|
175
179
|
content_link: str,
|
|
176
180
|
ontology_types: List[str],
|
|
177
181
|
entity_type: EntityType = EntityType.PUBLIC_ENTITY,
|
|
182
|
+
tokens: Optional[List[str]] = None,
|
|
183
|
+
token_indexes: Optional[List[int]] = None,
|
|
184
|
+
|
|
178
185
|
):
|
|
179
186
|
super().__init__(ref_text, start_idx, end_idx, entity_type)
|
|
180
187
|
self.__source: EntitySource = source
|
|
@@ -185,6 +192,8 @@ class KnowledgeGraphEntity(NamedEntity):
|
|
|
185
192
|
self.__thumbnail: Optional[str] = None
|
|
186
193
|
self.__ontology_types: List[str] = ontology_types
|
|
187
194
|
self.__relevant_type: OntologyClassReference = THING_CLASS
|
|
195
|
+
self.__tokens: Optional[List[str]] = tokens
|
|
196
|
+
self.__token_indexes: Optional[List[int]] = token_indexes
|
|
188
197
|
|
|
189
198
|
@property
|
|
190
199
|
def entity_source(self) -> EntitySource:
|
|
@@ -242,6 +251,16 @@ class KnowledgeGraphEntity(NamedEntity):
|
|
|
242
251
|
def relevant_type(self, value: OntologyClassReference):
|
|
243
252
|
self.__relevant_type = value
|
|
244
253
|
|
|
254
|
+
@property
|
|
255
|
+
def tokens(self) -> Optional[List[str]]:
|
|
256
|
+
"""List of tokens used to identify the entity."""
|
|
257
|
+
return self.__tokens
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def token_indexes(self) -> Optional[List[int]]:
|
|
261
|
+
"""List of token indexes used to identify the entity."""
|
|
262
|
+
return self.__token_indexes
|
|
263
|
+
|
|
245
264
|
def __repr__(self):
|
|
246
265
|
return f"{self.ref_text} [{self.start_idx}-{self.end_idx}] -> {self.entity_source} [{self.entity_type}]"
|
|
247
266
|
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/nel/engine.py
RENAMED
|
@@ -116,6 +116,8 @@ class WacomEntityLinkingEngine(PersonalEntityLinkingProcessor):
|
|
|
116
116
|
content_link="",
|
|
117
117
|
ontology_types=entity_types,
|
|
118
118
|
entity_type=EntityType.PERSONAL_ENTITY,
|
|
119
|
+
tokens=e.get("tokens"),
|
|
120
|
+
token_indexes=e.get("tokenIndexes"),
|
|
119
121
|
)
|
|
120
122
|
ne.relevant_type = OntologyClassReference.parse(e["type"])
|
|
121
123
|
named_entities.append(ne)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# Copyright © 2024-present Wacom. All rights reserved.
|
|
3
3
|
import asyncio
|
|
4
|
+
import gzip
|
|
5
|
+
import json
|
|
4
6
|
import logging
|
|
5
7
|
import os
|
|
6
8
|
import urllib
|
|
@@ -35,6 +37,7 @@ from knowledge.base.ontology import (
|
|
|
35
37
|
OntologyClassReference,
|
|
36
38
|
ObjectProperty,
|
|
37
39
|
)
|
|
40
|
+
from knowledge.base.response import JobStatus, ErrorLogResponse, NewEntityUrisResponse
|
|
38
41
|
from knowledge.nel.base import KnowledgeGraphEntity, EntityType, KnowledgeSource, EntitySource
|
|
39
42
|
from knowledge.services import AUTHORIZATION_HEADER_FLAG, IS_OWNER_PARAM, IndexType
|
|
40
43
|
from knowledge.services import (
|
|
@@ -114,6 +117,8 @@ class AsyncWacomKnowledgeService(AsyncServiceAPIClient):
|
|
|
114
117
|
SEARCH_DESCRIPTION_ENDPOINT: str = "semantic-search/description"
|
|
115
118
|
SEARCH_RELATION_ENDPOINT: str = "semantic-search/relation"
|
|
116
119
|
ONTOLOGY_UPDATE_ENDPOINT: str = "ontology-update"
|
|
120
|
+
IMPORT_ENTITIES_ENDPOINT: str = "import"
|
|
121
|
+
IMPORT_ERROR_LOG_ENDPOINT: str = "import/errorlog"
|
|
117
122
|
|
|
118
123
|
def __init__(
|
|
119
124
|
self,
|
|
@@ -180,6 +185,61 @@ class AsyncWacomKnowledgeService(AsyncServiceAPIClient):
|
|
|
180
185
|
thing: ThingObject = ThingObject.from_dict(e)
|
|
181
186
|
return thing
|
|
182
187
|
|
|
188
|
+
async def entities(self, uris: List[str], locale: Optional[LocaleCode] = None, auth_key: Optional[str] = None) \
|
|
189
|
+
-> List[ThingObject]:
|
|
190
|
+
"""
|
|
191
|
+
Retrieve entities information from personal knowledge, using the URI as identifier.
|
|
192
|
+
|
|
193
|
+
**Remark:** Object properties (relations) must be requested separately.
|
|
194
|
+
|
|
195
|
+
Parameters
|
|
196
|
+
----------
|
|
197
|
+
uris: List[str]
|
|
198
|
+
List of URIs of the entities
|
|
199
|
+
locale: LocaleCode
|
|
200
|
+
ISO-3166 Country Codes and ISO-639 Language Codes in the format <language_code>_<country>, e.g., en_US.
|
|
201
|
+
auth_key: Optional[str]
|
|
202
|
+
Use a different auth key than the one from the client
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
things: List[ThingObject]
|
|
207
|
+
Entities with is type URI, description, an image/icon, and tags (labels).
|
|
208
|
+
|
|
209
|
+
Raises
|
|
210
|
+
------
|
|
211
|
+
WacomServiceException
|
|
212
|
+
If the graph service returns an error code or the entity is not found in the knowledge graph
|
|
213
|
+
"""
|
|
214
|
+
if auth_key is None:
|
|
215
|
+
auth_key, _ = await self.handle_token()
|
|
216
|
+
url: str = f"{self.service_base_url}{AsyncWacomKnowledgeService.ENTITY_ENDPOINT}/"
|
|
217
|
+
headers: Dict[str, str] = {
|
|
218
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
219
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
220
|
+
}
|
|
221
|
+
things: List[ThingObject] = []
|
|
222
|
+
params: Dict[str, Any] = {
|
|
223
|
+
URIS_TAG: uris
|
|
224
|
+
}
|
|
225
|
+
if locale:
|
|
226
|
+
params[LOCALE_TAG] = locale
|
|
227
|
+
async with AsyncServiceAPIClient.__async_session__() as session:
|
|
228
|
+
async with session.get(url, headers=headers, params=params, verify_ssl=self.verify_calls) as response:
|
|
229
|
+
if response.ok:
|
|
230
|
+
entities: List[Dict[str, Any]] = await response.json()
|
|
231
|
+
for e in entities:
|
|
232
|
+
thing: ThingObject = ThingObject.from_dict(e)
|
|
233
|
+
things.append(thing)
|
|
234
|
+
else:
|
|
235
|
+
raise await handle_error(
|
|
236
|
+
f"Retrieving of entities content failed. List of URIs: {uris}.", response,
|
|
237
|
+
headers=headers
|
|
238
|
+
)
|
|
239
|
+
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
240
|
+
# Create ThingObject
|
|
241
|
+
return things
|
|
242
|
+
|
|
183
243
|
async def set_entity_image_local(self, entity_uri: str, path: Path, auth_key: Optional[str] = None) -> str:
|
|
184
244
|
"""Setting the image of the entity.
|
|
185
245
|
The image is stored locally.
|
|
@@ -533,6 +593,222 @@ class AsyncWacomKnowledgeService(AsyncServiceAPIClient):
|
|
|
533
593
|
await asyncio.sleep(0.25 if self.use_graceful_shutdown else 0.0)
|
|
534
594
|
return uri
|
|
535
595
|
|
|
596
|
+
async def import_entities(
|
|
597
|
+
self,
|
|
598
|
+
entities: List[ThingObject],
|
|
599
|
+
auth_key: Optional[str] = None,
|
|
600
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
601
|
+
) -> str:
|
|
602
|
+
"""Import entities to the graph.
|
|
603
|
+
|
|
604
|
+
Parameters
|
|
605
|
+
----------
|
|
606
|
+
entities: List[ThingObject]
|
|
607
|
+
List of entities to import.
|
|
608
|
+
auth_key: Optional[str] = None
|
|
609
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
610
|
+
timeout: int
|
|
611
|
+
Timeout for the request (default: 60 seconds)
|
|
612
|
+
|
|
613
|
+
Returns
|
|
614
|
+
-------
|
|
615
|
+
job_id: str
|
|
616
|
+
ID of the job
|
|
617
|
+
|
|
618
|
+
Raises
|
|
619
|
+
------
|
|
620
|
+
WacomServiceException
|
|
621
|
+
If the graph service returns an error code.
|
|
622
|
+
"""
|
|
623
|
+
if auth_key is None:
|
|
624
|
+
auth_key, _ = await self.handle_token()
|
|
625
|
+
headers: dict = {USER_AGENT_HEADER_FLAG: self.user_agent, AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}"}
|
|
626
|
+
ndjson_lines: List[str] = []
|
|
627
|
+
for obj in entities:
|
|
628
|
+
data_dict = obj.__import_format_dict__()
|
|
629
|
+
ndjson_lines.append(json.dumps(data_dict)) # Convert each dict to a JSON string
|
|
630
|
+
|
|
631
|
+
ndjson_content = "\n".join(ndjson_lines) # Join JSON strings with newline
|
|
632
|
+
|
|
633
|
+
# Compress the NDJSON string to a gzip byte array
|
|
634
|
+
compressed_data: bytes = gzip.compress(ndjson_content.encode("utf-8"))
|
|
635
|
+
url: str = f"{self.service_base_url}{self.IMPORT_ENTITIES_ENDPOINT}"
|
|
636
|
+
data: aiohttp.FormData = aiohttp.FormData()
|
|
637
|
+
data.add_field("file", compressed_data, filename="import.njson.gz", content_type="application/x-gzip")
|
|
638
|
+
async with AsyncServiceAPIClient.__async_session__() as session:
|
|
639
|
+
async with session.post(
|
|
640
|
+
url, headers=headers, data=data, timeout=timeout, verify_ssl=self.verify_calls
|
|
641
|
+
) as response:
|
|
642
|
+
if response.ok:
|
|
643
|
+
structure: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
644
|
+
return structure["jobId"]
|
|
645
|
+
raise await handle_error("Import endpoint returns an error.", response)
|
|
646
|
+
|
|
647
|
+
async def import_entities_from_file(
|
|
648
|
+
self,
|
|
649
|
+
file_path: Path,
|
|
650
|
+
auth_key: Optional[str] = None,
|
|
651
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
652
|
+
) -> str:
|
|
653
|
+
"""Import entities from a file to the graph.
|
|
654
|
+
|
|
655
|
+
Parameters
|
|
656
|
+
----------
|
|
657
|
+
file_path: Path
|
|
658
|
+
Path to the file containing entities in NDJSON format.
|
|
659
|
+
auth_key: Optional[str] = None
|
|
660
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
661
|
+
timeout: int
|
|
662
|
+
Timeout for the request (default: 60 seconds)
|
|
663
|
+
|
|
664
|
+
Returns
|
|
665
|
+
-------
|
|
666
|
+
job_id: str
|
|
667
|
+
ID of the job
|
|
668
|
+
|
|
669
|
+
Raises
|
|
670
|
+
------
|
|
671
|
+
WacomServiceException
|
|
672
|
+
If the graph service returns an error code.
|
|
673
|
+
"""
|
|
674
|
+
if not file_path.exists():
|
|
675
|
+
raise FileNotFoundError(f"The file {file_path} does not exist.")
|
|
676
|
+
if auth_key is None:
|
|
677
|
+
auth_key, _ = await self.handle_token()
|
|
678
|
+
headers: dict = {USER_AGENT_HEADER_FLAG: self.user_agent, AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}"}
|
|
679
|
+
with file_path.open("rb") as file:
|
|
680
|
+
# Compress the NDJSON string to a gzip byte array
|
|
681
|
+
compressed_data: bytes = file.read()
|
|
682
|
+
data: aiohttp.FormData = aiohttp.FormData()
|
|
683
|
+
data.add_field("file", compressed_data, filename="import.njson.gz",
|
|
684
|
+
content_type="application/x-gzip")
|
|
685
|
+
url: str = f"{self.service_base_url}{self.IMPORT_ENTITIES_ENDPOINT}"
|
|
686
|
+
async with AsyncServiceAPIClient.__async_session__() as session:
|
|
687
|
+
async with session.post(
|
|
688
|
+
url, headers=headers, data=data, timeout=timeout, verify_ssl=self.verify_calls
|
|
689
|
+
) as response:
|
|
690
|
+
if response.ok:
|
|
691
|
+
structure: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
692
|
+
return structure["jobId"]
|
|
693
|
+
raise await handle_error("Import endpoint returns an error.", response)
|
|
694
|
+
|
|
695
|
+
async def job_status(
|
|
696
|
+
self,
|
|
697
|
+
job_id: str,
|
|
698
|
+
auth_key: Optional[str] = None,
|
|
699
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
700
|
+
) -> JobStatus:
|
|
701
|
+
"""
|
|
702
|
+
Retrieve the status of the job.
|
|
703
|
+
|
|
704
|
+
Parameters
|
|
705
|
+
----------
|
|
706
|
+
job_id: str
|
|
707
|
+
ID of the job
|
|
708
|
+
auth_key: Optional[str] = None
|
|
709
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
710
|
+
timeout: int
|
|
711
|
+
Timeout for the request (default: 60 seconds)
|
|
712
|
+
|
|
713
|
+
Returns
|
|
714
|
+
-------
|
|
715
|
+
job_status: JobStatus
|
|
716
|
+
Status of the job
|
|
717
|
+
"""
|
|
718
|
+
if auth_key is None:
|
|
719
|
+
auth_key, _ = await self.handle_token()
|
|
720
|
+
headers: dict = {USER_AGENT_HEADER_FLAG: self.user_agent, AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}"}
|
|
721
|
+
url: str = f"{self.service_base_url}{self.IMPORT_ENTITIES_ENDPOINT}/{job_id}"
|
|
722
|
+
async with AsyncServiceAPIClient.__async_session__() as session:
|
|
723
|
+
async with session.get(
|
|
724
|
+
url, headers=headers, timeout=timeout, verify_ssl=self.verify_calls
|
|
725
|
+
) as response:
|
|
726
|
+
if response.ok:
|
|
727
|
+
structure: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
728
|
+
return JobStatus.from_dict(structure)
|
|
729
|
+
raise await handle_error(f"Retrieving job status for {job_id} failed.", response, headers=headers)
|
|
730
|
+
|
|
731
|
+
async def import_error_log(
|
|
732
|
+
self,
|
|
733
|
+
job_id: str,
|
|
734
|
+
auth_key: Optional[str] = None,
|
|
735
|
+
next_page_id: Optional[str] = None,
|
|
736
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
737
|
+
) -> ErrorLogResponse:
|
|
738
|
+
"""
|
|
739
|
+
Retrieve the error log of the job.
|
|
740
|
+
|
|
741
|
+
Parameters
|
|
742
|
+
----------
|
|
743
|
+
job_id: str
|
|
744
|
+
ID of the job
|
|
745
|
+
next_page_id: Optional[str] = None
|
|
746
|
+
ID of the next page within pagination.
|
|
747
|
+
auth_key: Optional[str] = None
|
|
748
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
749
|
+
timeout: int
|
|
750
|
+
Timeout for the request (default: 60 seconds)
|
|
751
|
+
|
|
752
|
+
Returns
|
|
753
|
+
-------
|
|
754
|
+
error: ErrorLogResponse
|
|
755
|
+
Error log of the job
|
|
756
|
+
"""
|
|
757
|
+
if auth_key is None:
|
|
758
|
+
auth_key, _ = await self.handle_token()
|
|
759
|
+
headers: dict = {USER_AGENT_HEADER_FLAG: self.user_agent, AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}"}
|
|
760
|
+
url: str = f"{self.service_base_url}{self.IMPORT_ERROR_LOG_ENDPOINT}/{job_id}"
|
|
761
|
+
params: Dict[str, str] = {NEXT_PAGE_ID_TAG: next_page_id} if next_page_id else {}
|
|
762
|
+
async with AsyncServiceAPIClient.__async_session__() as session:
|
|
763
|
+
async with session.get(
|
|
764
|
+
url, headers=headers, params=params, timeout=timeout, verify_ssl=self.verify_calls
|
|
765
|
+
) as response:
|
|
766
|
+
if response.ok:
|
|
767
|
+
structure: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
768
|
+
return ErrorLogResponse.from_dict(structure)
|
|
769
|
+
raise await handle_error(f"Retrieving job status for {job_id} failed.", response)
|
|
770
|
+
|
|
771
|
+
async def import_new_uris(
|
|
772
|
+
self,
|
|
773
|
+
job_id: str,
|
|
774
|
+
auth_key: Optional[str] = None,
|
|
775
|
+
next_page_id: Optional[str] = None,
|
|
776
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
777
|
+
) -> NewEntityUrisResponse:
|
|
778
|
+
"""
|
|
779
|
+
Retrieve the new entity uris from the job.
|
|
780
|
+
|
|
781
|
+
Parameters
|
|
782
|
+
----------
|
|
783
|
+
job_id: str
|
|
784
|
+
ID of the job
|
|
785
|
+
next_page_id: Optional[str] = None
|
|
786
|
+
ID of the next page within pagination.
|
|
787
|
+
auth_key: Optional[str] = None
|
|
788
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
789
|
+
timeout: int
|
|
790
|
+
Timeout for the request (default: 60 seconds)
|
|
791
|
+
|
|
792
|
+
Returns
|
|
793
|
+
-------
|
|
794
|
+
response: NewEntityUrisResponse
|
|
795
|
+
New entity uris of the job.
|
|
796
|
+
"""
|
|
797
|
+
if auth_key is None:
|
|
798
|
+
auth_key, _ = await self.handle_token()
|
|
799
|
+
headers: dict = {USER_AGENT_HEADER_FLAG: self.user_agent, AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}"}
|
|
800
|
+
url: str = f"{self.service_base_url}{self.IMPORT_ENTITIES_ENDPOINT}/{job_id}/new-entities"
|
|
801
|
+
params: Dict[str, str] = {NEXT_PAGE_ID_TAG: next_page_id} if next_page_id else {}
|
|
802
|
+
async with AsyncServiceAPIClient.__async_session__() as session:
|
|
803
|
+
async with session.get(
|
|
804
|
+
url, headers=headers, params=params, timeout=timeout, verify_ssl=self.verify_calls
|
|
805
|
+
) as response:
|
|
806
|
+
if response.ok:
|
|
807
|
+
structure: Dict[str, Any] = await response.json(loads=orjson.loads)
|
|
808
|
+
return NewEntityUrisResponse.from_dict(structure)
|
|
809
|
+
raise await handle_error(f"Retrieving job status for {job_id} failed.", response)
|
|
810
|
+
|
|
811
|
+
|
|
536
812
|
async def update_entity(self, entity: ThingObject, auth_key: Optional[str] = None):
|
|
537
813
|
"""
|
|
538
814
|
Updates entity in graph.
|
|
@@ -1521,6 +1797,8 @@ class AsyncWacomKnowledgeService(AsyncServiceAPIClient):
|
|
|
1521
1797
|
content_link="",
|
|
1522
1798
|
ontology_types=entity_types,
|
|
1523
1799
|
entity_type=EntityType.PERSONAL_ENTITY,
|
|
1800
|
+
tokens=e.get("token"),
|
|
1801
|
+
token_indexes=e.get("tokenIndexes"),
|
|
1524
1802
|
)
|
|
1525
1803
|
ne.relevant_type = OntologyClassReference.parse(e["type"])
|
|
1526
1804
|
named_entities.append(ne)
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/base.py
RENAMED
|
@@ -300,7 +300,8 @@ class WacomServiceAPIClient(RESTAPIClient):
|
|
|
300
300
|
raise WacomServiceException(f"Unknown session id:= {self.__current_session_id}. Please login first.")
|
|
301
301
|
return session
|
|
302
302
|
|
|
303
|
-
def request_user_token(self, tenant_api_key: str, external_id: str
|
|
303
|
+
def request_user_token(self, tenant_api_key: str, external_id: str, timeout: int = DEFAULT_TIMEOUT) \
|
|
304
|
+
-> Tuple[str, str, datetime]:
|
|
304
305
|
"""
|
|
305
306
|
Login as user by using the tenant key and its external user id.
|
|
306
307
|
|
|
@@ -310,6 +311,8 @@ class WacomServiceAPIClient(RESTAPIClient):
|
|
|
310
311
|
Tenant API key
|
|
311
312
|
external_id: str
|
|
312
313
|
External id.
|
|
314
|
+
timeout: int (Default:= DEFAULT_TIMEOUT)
|
|
315
|
+
Timeout for the request in seconds.
|
|
313
316
|
|
|
314
317
|
Returns
|
|
315
318
|
-------
|
|
@@ -333,7 +336,8 @@ class WacomServiceAPIClient(RESTAPIClient):
|
|
|
333
336
|
}
|
|
334
337
|
payload: dict = {EXTERNAL_USER_ID: external_id}
|
|
335
338
|
response: Response = requests.post(
|
|
336
|
-
url, headers=headers, json=payload, timeout=
|
|
339
|
+
url, headers=headers, json=payload, timeout=timeout, verify=self.verify_calls,
|
|
340
|
+
allow_redirects=True
|
|
337
341
|
)
|
|
338
342
|
if response.ok:
|
|
339
343
|
try:
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/graph.py
RENAMED
|
@@ -18,7 +18,6 @@ from knowledge.base.entity import (
|
|
|
18
18
|
DATA_PROPERTIES_TAG,
|
|
19
19
|
TYPE_TAG,
|
|
20
20
|
LABELS_TAG,
|
|
21
|
-
IS_MAIN_TAG,
|
|
22
21
|
RELATIONS_TAG,
|
|
23
22
|
LOCALE_TAG,
|
|
24
23
|
EntityStatus,
|
|
@@ -226,18 +225,74 @@ class WacomKnowledgeService(WacomServiceAPIClient):
|
|
|
226
225
|
response: Response = session.get(url, headers=headers, timeout=timeout, verify=self.verify_calls)
|
|
227
226
|
if response.ok:
|
|
228
227
|
e: Dict[str, Any] = response.json()
|
|
229
|
-
pref_label: List[Label] = []
|
|
230
|
-
aliases: List[Label] = []
|
|
231
|
-
# Extract labels and alias
|
|
232
|
-
for label in e[LABELS_TAG]:
|
|
233
|
-
if label[IS_MAIN_TAG]: # Labels
|
|
234
|
-
pref_label.append(Label.create_from_dict(label))
|
|
235
|
-
else: # Alias
|
|
236
|
-
aliases.append(Label.create_from_dict(label))
|
|
237
228
|
thing: ThingObject = ThingObject.from_dict(e)
|
|
238
229
|
return thing
|
|
239
230
|
raise handle_error(f"Retrieving of entity content failed. URI:={uri}.", response)
|
|
240
231
|
|
|
232
|
+
def entities(self,
|
|
233
|
+
uris: List[str],
|
|
234
|
+
locale: Optional[LocaleCode] = None,
|
|
235
|
+
auth_key: Optional[str] = None,
|
|
236
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
237
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
238
|
+
backoff_factor: float = DEFAULT_BACKOFF_FACTOR,
|
|
239
|
+
) -> List[ThingObject]:
|
|
240
|
+
"""
|
|
241
|
+
Retrieve entity information from personal knowledge, using the URI as identifier.
|
|
242
|
+
|
|
243
|
+
**Remark:** Object properties (relations) must be requested separately.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
uris: List[str]
|
|
248
|
+
List of URIs of entities
|
|
249
|
+
locale: Optional[LocaleCode]
|
|
250
|
+
ISO-3166 Country Codes and ISO-639 Language Codes in the format <language_code>_<country>, e.g., en_US.
|
|
251
|
+
auth_key: Optional[str]
|
|
252
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
253
|
+
timeout: int
|
|
254
|
+
Timeout for the request (default: 60 seconds)
|
|
255
|
+
max_retries: int
|
|
256
|
+
Maximum number of retries (default: 3)
|
|
257
|
+
backoff_factor: float
|
|
258
|
+
A backoff factor to apply between attempts after the second try (most errors are resolved immediately by a
|
|
259
|
+
second try without a delay) (default: 0.1)
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
things: List[ThingObject]
|
|
264
|
+
Entities with is type URI, description, an image/icon, and tags (labels).
|
|
265
|
+
|
|
266
|
+
Raises
|
|
267
|
+
------
|
|
268
|
+
WacomServiceException
|
|
269
|
+
If the graph service returns an error code or the entity is not found in the knowledge graph
|
|
270
|
+
"""
|
|
271
|
+
if auth_key is None:
|
|
272
|
+
auth_key, _ = self.handle_token()
|
|
273
|
+
url: str = f"{self.service_base_url}{WacomKnowledgeService.ENTITY_ENDPOINT}/"
|
|
274
|
+
headers: Dict[str, str] = {
|
|
275
|
+
USER_AGENT_HEADER_FLAG: self.user_agent,
|
|
276
|
+
AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}",
|
|
277
|
+
}
|
|
278
|
+
params: Dict[str, Any] = {URIS_TAG: uris}
|
|
279
|
+
if locale is not None:
|
|
280
|
+
params[LOCALE_TAG] = locale
|
|
281
|
+
mount_point: str = "https://" if self.service_url.startswith("https") else "http://"
|
|
282
|
+
with requests.Session() as session:
|
|
283
|
+
retries: Retry = Retry(total=max_retries, backoff_factor=backoff_factor, status_forcelist=STATUS_FORCE_LIST)
|
|
284
|
+
session.mount(mount_point, HTTPAdapter(max_retries=retries))
|
|
285
|
+
response: Response = session.get(url, params=params, headers=headers, timeout=timeout,
|
|
286
|
+
verify=self.verify_calls)
|
|
287
|
+
if response.ok:
|
|
288
|
+
things: List[ThingObject] = []
|
|
289
|
+
entities: List[Dict[str, Any]] = response.json()
|
|
290
|
+
for e in entities:
|
|
291
|
+
thing: ThingObject = ThingObject.from_dict(e)
|
|
292
|
+
things.append(thing)
|
|
293
|
+
return things
|
|
294
|
+
raise handle_error(f"Retrieving of entity content failed. URIs:={uris}.", response)
|
|
295
|
+
|
|
241
296
|
def delete_entities(
|
|
242
297
|
self,
|
|
243
298
|
uris: List[str],
|
|
@@ -520,6 +575,8 @@ class WacomKnowledgeService(WacomServiceAPIClient):
|
|
|
520
575
|
if entity.image is not None and entity.image.startswith("file:"):
|
|
521
576
|
p = urlparse(entity.image)
|
|
522
577
|
self.set_entity_image_local(uri, Path(p.path), auth_key=auth_key)
|
|
578
|
+
elif entity.image is not None and entity.image.startswith("/"):
|
|
579
|
+
self.set_entity_image_local(uri, Path(entity.image), auth_key=auth_key)
|
|
523
580
|
elif entity.image is not None and entity.image != "":
|
|
524
581
|
self.set_entity_image_url(uri, entity.image, auth_key=auth_key)
|
|
525
582
|
except WacomServiceException as _:
|
|
@@ -1824,6 +1881,63 @@ class WacomKnowledgeService(WacomServiceAPIClient):
|
|
|
1824
1881
|
return response.json()["jobId"]
|
|
1825
1882
|
raise handle_error("Import endpoint returns an error.", response)
|
|
1826
1883
|
|
|
1884
|
+
def import_entities_from_file(
|
|
1885
|
+
self,
|
|
1886
|
+
file_path: Path,
|
|
1887
|
+
auth_key: Optional[str] = None,
|
|
1888
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
1889
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
1890
|
+
backoff_factor: float = DEFAULT_BACKOFF_FACTOR,
|
|
1891
|
+
) -> str:
|
|
1892
|
+
"""Import entities from a file to the graph.
|
|
1893
|
+
|
|
1894
|
+
Parameters
|
|
1895
|
+
----------
|
|
1896
|
+
file_path: Path
|
|
1897
|
+
Path to the file containing entities in NDJSON format.
|
|
1898
|
+
auth_key: Optional[str] = None
|
|
1899
|
+
If the auth key is set the logged-in user (if any) will be ignored and the auth key will be used.
|
|
1900
|
+
timeout: int
|
|
1901
|
+
Timeout for the request (default: 60 seconds)
|
|
1902
|
+
max_retries: int
|
|
1903
|
+
Maximum number of retries
|
|
1904
|
+
backoff_factor: float
|
|
1905
|
+
A backoff factor to apply between attempts after the second try (most errors are resolved immediately by a
|
|
1906
|
+
second try without a delay)
|
|
1907
|
+
|
|
1908
|
+
Returns
|
|
1909
|
+
-------
|
|
1910
|
+
job_id: str
|
|
1911
|
+
ID of the job
|
|
1912
|
+
|
|
1913
|
+
Raises
|
|
1914
|
+
------
|
|
1915
|
+
WacomServiceException
|
|
1916
|
+
If the graph service returns an error code.
|
|
1917
|
+
"""
|
|
1918
|
+
if not file_path.exists():
|
|
1919
|
+
raise FileNotFoundError(f"The file {file_path} does not exist.")
|
|
1920
|
+
if auth_key is None:
|
|
1921
|
+
auth_key, _ = self.handle_token()
|
|
1922
|
+
headers: dict = {USER_AGENT_HEADER_FLAG: self.user_agent, AUTHORIZATION_HEADER_FLAG: f"Bearer {auth_key}"}
|
|
1923
|
+
with file_path.open("rb") as file:
|
|
1924
|
+
# Compress the NDJSON string to a gzip byte array
|
|
1925
|
+
compressed_data: bytes = file.read()
|
|
1926
|
+
files: List[Tuple[str, Tuple[str, bytes, str]]] = [
|
|
1927
|
+
("file", ("import.njson.gz", compressed_data, "application/x-gzip"))
|
|
1928
|
+
]
|
|
1929
|
+
url: str = f"{self.service_base_url}{self.IMPORT_ENTITIES_ENDPOINT}"
|
|
1930
|
+
mount_point: str = "https://" if self.service_url.startswith("https") else "http://"
|
|
1931
|
+
with requests.Session() as session:
|
|
1932
|
+
retries: Retry = Retry(total=max_retries, backoff_factor=backoff_factor, status_forcelist=STATUS_FORCE_LIST)
|
|
1933
|
+
session.mount(mount_point, HTTPAdapter(max_retries=retries))
|
|
1934
|
+
response: Response = session.post(
|
|
1935
|
+
url, headers=headers, files=files, timeout=timeout, verify=self.verify_calls
|
|
1936
|
+
)
|
|
1937
|
+
if response.ok:
|
|
1938
|
+
return response.json()["jobId"]
|
|
1939
|
+
raise handle_error("Import endpoint returns an error.", response)
|
|
1940
|
+
|
|
1827
1941
|
def job_status(
|
|
1828
1942
|
self,
|
|
1829
1943
|
job_id: str,
|
|
@@ -2017,7 +2131,7 @@ class WacomKnowledgeService(WacomServiceAPIClient):
|
|
|
2017
2131
|
|
|
2018
2132
|
def rebuild_nel_index(
|
|
2019
2133
|
self,
|
|
2020
|
-
nel_index: Literal["
|
|
2134
|
+
nel_index: Literal["Western", "Japanese"],
|
|
2021
2135
|
prune: bool = False,
|
|
2022
2136
|
auth_key: Optional[str] = None,
|
|
2023
2137
|
timeout: int = DEFAULT_TIMEOUT,
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/search.py
RENAMED
|
@@ -263,6 +263,7 @@ class SemanticSearchClient(WacomServiceAPIClient):
|
|
|
263
263
|
locale: LocaleCode,
|
|
264
264
|
concept_type: Optional[str] = None,
|
|
265
265
|
auth_key: Optional[str] = None,
|
|
266
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
266
267
|
max_retries: int = 3,
|
|
267
268
|
backoff_factor: float = 0.1,
|
|
268
269
|
) -> int:
|
|
@@ -275,6 +276,8 @@ class SemanticSearchClient(WacomServiceAPIClient):
|
|
|
275
276
|
ISO-3166 Country Codes and ISO-639 Language Codes in the format '<language_code>_<country>', e.g., en_US.
|
|
276
277
|
concept_type: Optional[str] (Default:= None)
|
|
277
278
|
Concept type.
|
|
279
|
+
timeout: int (Default:= DEFAULT_TIMEOUT)
|
|
280
|
+
Timeout for the request in seconds.
|
|
278
281
|
max_retries: int
|
|
279
282
|
Maximum number of retries
|
|
280
283
|
backoff_factor: float
|
|
@@ -306,7 +309,7 @@ class SemanticSearchClient(WacomServiceAPIClient):
|
|
|
306
309
|
with requests.Session() as session:
|
|
307
310
|
retries: Retry = Retry(total=max_retries, backoff_factor=backoff_factor, status_forcelist=STATUS_FORCE_LIST)
|
|
308
311
|
session.mount(mount_point, HTTPAdapter(max_retries=retries))
|
|
309
|
-
response = session.get(url, params=params, headers=headers)
|
|
312
|
+
response = session.get(url, params=params, headers=headers, timeout=timeout)
|
|
310
313
|
if response.ok:
|
|
311
314
|
return response.json().get("count", 0)
|
|
312
315
|
raise handle_error("Counting labels failed.", response, headers=headers, parameters={"locale": locale})
|
|
File without changes
|
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/__init__.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/access.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/entity.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/language.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/response.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/search.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/base/tenant.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/nel/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/__init__.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/cache.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/client.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/helper.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/relations.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/public/wikidata.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/group.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/helper.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/ontology.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/session.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/tenant.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/services/users.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/__init__.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/diff.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/graph.py
RENAMED
|
File without changes
|
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/wikidata.py
RENAMED
|
File without changes
|
{personal_knowledge_library-3.2.1 → personal_knowledge_library-3.3.0}/knowledge/utils/wikipedia.py
RENAMED
|
File without changes
|