nucliadb-sdk 0.0.0.post2__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.
@@ -0,0 +1,369 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ import base64
21
+ from typing import Callable, Dict, List, Optional, Union, cast
22
+ from uuid import uuid4
23
+
24
+ import numpy as np
25
+
26
+ from nucliadb_models.common import Classification, FieldID
27
+ from nucliadb_models.common import File as NDBModelsFile
28
+ from nucliadb_models.file import FileField
29
+ from nucliadb_models.link import LinkField
30
+ from nucliadb_models.metadata import Origin, TokenSplit, UserFieldMetadata, UserMetadata
31
+ from nucliadb_models.resource import Resource
32
+ from nucliadb_models.text import TextField
33
+ from nucliadb_models.utils import FieldIdString, SlugString
34
+ from nucliadb_models.vectors import UserVector, UserVectorWrapper, VectorSet, VectorSets
35
+ from nucliadb_models.writer import (
36
+ GENERIC_MIME_TYPE,
37
+ CreateResourcePayload,
38
+ UpdateResourcePayload,
39
+ )
40
+ from nucliadb_sdk import DEFAULT_LABELSET, logger
41
+ from nucliadb_sdk.entities import Entities
42
+ from nucliadb_sdk.file import File
43
+ from nucliadb_sdk.labels import Label, Labels
44
+ from nucliadb_sdk.vectors import Vector, Vectors, convert_vector
45
+
46
+
47
+ def create_resource(
48
+ key: Optional[str] = None,
49
+ text: Optional[str] = None,
50
+ binary: Optional[Union[File, str]] = None,
51
+ labels: Optional[Labels] = None,
52
+ entities: Optional[Entities] = None,
53
+ vectors: Optional[Union[Vectors, Dict[str, Union[np.ndarray, List[float]]]]] = None,
54
+ vectorsets: Optional[VectorSets] = None,
55
+ icon: Optional[str] = None,
56
+ ) -> CreateResourcePayload:
57
+ create_payload = CreateResourcePayload()
58
+ create_payload.origin = Origin(source=Origin.Source.PYSDK)
59
+ if key is not None:
60
+ create_payload.slug = SlugString(key)
61
+ if icon is not None:
62
+ create_payload.icon = icon
63
+ else:
64
+ create_payload.icon = GENERIC_MIME_TYPE
65
+ main_field = None
66
+ if text is not None:
67
+ create_payload.texts[FieldIdString("text")] = TextField(body=text)
68
+ main_field = FieldID(field_type=FieldID.FieldType.TEXT, field="text")
69
+ if binary is not None:
70
+ if isinstance(binary, str):
71
+ with open(binary, "rb") as binary_file:
72
+ data = binary_file.read()
73
+ binary = File(data=data, filename=binary.split("/")[-1])
74
+ assert isinstance(binary, File)
75
+
76
+ create_payload.files[FieldIdString("file")] = FileField(
77
+ file=NDBModelsFile(
78
+ filename=binary.filename,
79
+ content_type=binary.content_type,
80
+ payload=base64.b64encode(binary.data),
81
+ )
82
+ )
83
+ if main_field is None:
84
+ main_field = FieldID(field_type=FieldID.FieldType.FILE, field="file")
85
+
86
+ if main_field is None:
87
+ raise AttributeError("Missing field")
88
+
89
+ if labels is not None:
90
+ classifications = []
91
+ for label in labels:
92
+ if isinstance(label, Label):
93
+ classifications.append(
94
+ Classification(
95
+ labelset=label.labelset
96
+ if label.labelset is not None
97
+ else DEFAULT_LABELSET,
98
+ label=label.label,
99
+ )
100
+ )
101
+ elif isinstance(label, str):
102
+ if label.count("/") != 1:
103
+ labelset = DEFAULT_LABELSET
104
+ label_str = label
105
+ else:
106
+ labelset, label_str = label.split("/")
107
+ classifications.append(
108
+ Classification(labelset=labelset, label=label_str)
109
+ )
110
+
111
+ create_payload.usermetadata = UserMetadata(classifications=classifications)
112
+
113
+ if entities is not None and text is not None:
114
+ tokens = []
115
+ for entity in entities:
116
+ for position in entity.positions:
117
+ tokens.append(
118
+ TokenSplit(
119
+ token=entity.value,
120
+ klass=entity.type,
121
+ start=position[0],
122
+ end=position[1],
123
+ )
124
+ )
125
+
126
+ create_payload.fieldmetadata = []
127
+ create_payload.fieldmetadata.append(
128
+ UserFieldMetadata(
129
+ token=tokens,
130
+ field=main_field,
131
+ )
132
+ )
133
+
134
+ if vectors is not None:
135
+ if isinstance(vectors, dict):
136
+ new_vectors = []
137
+ for key, value in vectors.items():
138
+ list_value = convert_vector(value)
139
+ new_vectors.append(Vector(value=list_value, vectorset=key))
140
+ vectors = new_vectors
141
+ elif isinstance(vectors, list):
142
+ for vector_element in vectors:
143
+ vector_element.value = convert_vector(vector_element.value)
144
+
145
+ uvsw = []
146
+ uvw = UserVectorWrapper(field=main_field)
147
+ uvw.vectors = {}
148
+ generic_positions = (0, len(text) if text is not None else 0)
149
+ for vector in vectors:
150
+ vector_id = vector.key if vector.key is not None else uuid4().hex
151
+ if vectorsets is not None and vector.vectorset not in vectorsets.vectorsets:
152
+ logger.warn("Vectorset is not created, we will create it for you")
153
+ vectorsets.vectorsets[vector.vectorset] = VectorSet(
154
+ dimension=len(vector.value)
155
+ )
156
+ uvw.vectors[vector.vectorset] = {
157
+ vector_id: UserVector(
158
+ vector=vector.value,
159
+ positions=generic_positions
160
+ if vector.positions is None
161
+ else vector.positions,
162
+ )
163
+ }
164
+ uvsw.append(uvw)
165
+ create_payload.uservectors = uvsw
166
+
167
+ return create_payload
168
+
169
+
170
+ def update_resource(
171
+ resource: Resource,
172
+ text: Optional[str] = None,
173
+ binary: Optional[Union[File, str]] = None,
174
+ labels: Optional[Labels] = None,
175
+ entities: Optional[Entities] = None,
176
+ vectors: Optional[Union[Vectors, Dict[str, Union[np.ndarray, List[float]]]]] = None,
177
+ vectorsets: Optional[VectorSets] = None,
178
+ ) -> UpdateResourcePayload:
179
+ upload_payload = UpdateResourcePayload()
180
+
181
+ main_field = None
182
+ if text is not None:
183
+ upload_payload.texts[FieldIdString("text")] = TextField(body=text)
184
+ main_field = FieldID(field_type=FieldID.FieldType.TEXT, field="text")
185
+ if binary is not None:
186
+ if isinstance(binary, str):
187
+ with open(binary, "rb") as binary_file:
188
+ data = binary_file.read()
189
+ binary = File(data=data, filename=binary.split("/")[-1])
190
+
191
+ assert isinstance(binary, File)
192
+ upload_payload.files[FieldIdString("file")] = FileField(
193
+ file=NDBModelsFile(
194
+ filename=binary.filename,
195
+ content_type=binary.content_type,
196
+ payload=base64.b64encode(binary.data),
197
+ )
198
+ )
199
+ main_field = FieldID(field_type=FieldID.FieldType.FILE, field="file")
200
+
201
+ if main_field is None and resource.data is not None:
202
+ if (
203
+ resource.data.texts is not None
204
+ and FieldIdString("text") in resource.data.texts
205
+ ):
206
+ main_field = FieldID(field_type=FieldID.FieldType.TEXT, field="text")
207
+
208
+ elif (
209
+ resource.data.files is not None
210
+ and FieldIdString("file") in resource.data.files
211
+ ):
212
+ main_field = FieldID(field_type=FieldID.FieldType.FILE, field="file")
213
+
214
+ if labels is not None:
215
+ classifications = []
216
+ for label in labels:
217
+ if isinstance(label, Label):
218
+ classifications.append(
219
+ Classification(
220
+ labelset=label.labelset
221
+ if label.labelset is not None
222
+ else DEFAULT_LABELSET,
223
+ label=label.label,
224
+ )
225
+ )
226
+ elif isinstance(label, str):
227
+ if label.count("/") != 1:
228
+ logger.warn(f"Labelset default linked to label {label}")
229
+ labelset = DEFAULT_LABELSET
230
+ label_str = label
231
+ else:
232
+ labelset, label_str = label.split("/")
233
+
234
+ classifications.append(
235
+ Classification(labelset=labelset, label=label_str)
236
+ )
237
+
238
+ upload_payload.usermetadata = UserMetadata(classifications=classifications)
239
+
240
+ if entities is not None and text is not None:
241
+ tokens = []
242
+ for entity in entities:
243
+ for position in entity.positions:
244
+ tokens.append(
245
+ TokenSplit(
246
+ token=entity.value,
247
+ klass=entity.type,
248
+ start=position[0],
249
+ end=position[1],
250
+ )
251
+ )
252
+
253
+ upload_payload.fieldmetadata = []
254
+ upload_payload.fieldmetadata.append(
255
+ UserFieldMetadata(
256
+ token=tokens,
257
+ field=FieldID(field_type=FieldID.FieldType.TEXT, field="text"),
258
+ )
259
+ )
260
+
261
+ if vectors is not None:
262
+ if isinstance(vectors, dict):
263
+ new_vectors = []
264
+ for key, value in vectors.items():
265
+ list_value = convert_vector(value)
266
+ new_vectors.append(Vector(value=list_value, vectorset=key))
267
+ vectors = new_vectors
268
+
269
+ uvsw = []
270
+ uvw = UserVectorWrapper(field=main_field)
271
+ uvw.vectors = {}
272
+ generic_positions = (0, len(text) if text is not None else 0)
273
+ for vector in vectors:
274
+ vector_id = vector.key if vector.key is not None else uuid4().hex
275
+ if vectorsets is not None and vector.vectorset not in vectorsets.vectorsets:
276
+ logger.warn("Vectorset is not created, we will create it for you")
277
+ vectorsets.vectorsets[vector.vectorset] = VectorSet(
278
+ dimension=len(vector.value)
279
+ )
280
+
281
+ uvw.vectors[vector.vectorset] = {
282
+ vector_id: UserVector(
283
+ vector=vector.value,
284
+ positions=generic_positions
285
+ if vector.positions is None
286
+ else vector.positions,
287
+ )
288
+ }
289
+ uvsw.append(uvw)
290
+ upload_payload.uservectors = uvsw
291
+
292
+ return upload_payload
293
+
294
+
295
+ def from_resource_to_payload(
296
+ item: Resource,
297
+ download: Callable[[str], bytes],
298
+ update: bool = False,
299
+ ):
300
+ if update:
301
+ payload: Union[
302
+ UpdateResourcePayload, CreateResourcePayload
303
+ ] = UpdateResourcePayload()
304
+ else:
305
+ payload = CreateResourcePayload()
306
+
307
+ payload.slug = item.slug # type: ignore
308
+ payload.title = item.title
309
+ payload.summary = item.summary
310
+ if item.icon is not None:
311
+ payload.icon = item.icon
312
+ payload.thumbnail = item.thumbnail
313
+ payload.layout = item.layout
314
+
315
+ payload.usermetadata = item.usermetadata
316
+ payload.fieldmetadata = item.fieldmetadata
317
+
318
+ payload.origin = item.origin
319
+
320
+ if item.data is not None and item.data.texts is not None:
321
+ for field, field_payload in item.data.texts.items():
322
+ if field_payload is not None and field_payload.value is not None:
323
+ payload.texts[cast(FieldIdString, field)] = TextField(
324
+ body=field_payload.value.body, format=field_payload.value.format
325
+ )
326
+
327
+ if item.data is not None and item.data.links is not None:
328
+ for field, link_payload in item.data.links.items():
329
+ if link_payload is not None and link_payload.value is not None:
330
+ payload.links[cast(FieldIdString, field)] = LinkField(
331
+ uri=link_payload.value.uri,
332
+ headers=link_payload.value.headers,
333
+ cookies=link_payload.value.cookies,
334
+ language=link_payload.value.language,
335
+ localstorage=link_payload.value.localstorage,
336
+ )
337
+
338
+ if item.data is not None and item.data.files is not None:
339
+ for field, file_payload in item.data.files.items():
340
+ if (
341
+ file_payload.value is not None
342
+ and file_payload.value is not None
343
+ and file_payload.value.file is not None
344
+ and file_payload.value.file.uri is not None
345
+ ):
346
+ data = download(file_payload.value.file.uri)
347
+ payload.files[cast(FieldIdString, field)] = FileField(
348
+ language=file_payload.value.language,
349
+ password=file_payload.value.password,
350
+ file=NDBModelsFile(
351
+ payload=base64.b64encode(data),
352
+ filename=file_payload.value.file.filename,
353
+ content_type=file_payload.value.file.content_type,
354
+ ),
355
+ )
356
+
357
+ if item.data is not None and item.data.layouts is not None:
358
+ raise NotImplementedError()
359
+
360
+ if item.data is not None and item.data.conversations is not None:
361
+ raise NotImplementedError()
362
+
363
+ if item.data is not None and item.data.keywordsets is not None:
364
+ raise NotImplementedError()
365
+
366
+ if item.data is not None and item.data.datetimes is not None:
367
+ raise NotImplementedError()
368
+
369
+ return payload
nucliadb_sdk/search.py ADDED
@@ -0,0 +1,104 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ from dataclasses import dataclass
21
+ from enum import Enum
22
+ from typing import Iterator, List
23
+
24
+ from nucliadb_models.search import KnowledgeboxSearchResults
25
+ from nucliadb_sdk.client import NucliaDBClient
26
+
27
+
28
+ class ScoreType(str, Enum):
29
+ BM25 = "BM25"
30
+ COSINE = "COSINE"
31
+
32
+
33
+ @dataclass
34
+ class SearchResource:
35
+ key: str
36
+ text: str
37
+ labels: List[str]
38
+ score: float
39
+ score_type: ScoreType
40
+
41
+
42
+ class SearchResult:
43
+ inner_search_results: KnowledgeboxSearchResults
44
+
45
+ def __init__(
46
+ self, inner_search_results: KnowledgeboxSearchResults, client: NucliaDBClient
47
+ ):
48
+ self.inner_search_results = inner_search_results
49
+ self.client = client
50
+
51
+ def __iter__(self) -> Iterator[SearchResource]:
52
+ if self.inner_search_results.fulltext is not None:
53
+ for fts in self.inner_search_results.fulltext.results:
54
+ resource = self.client.get_resource(fts.rid)
55
+ if fts.field_type == "t":
56
+ text = resource.data.texts[fts.field].value.body
57
+ classifications = [
58
+ classification.label
59
+ for classification in resource.usermetadata.classifications
60
+ ]
61
+ yield SearchResource(
62
+ text=text,
63
+ labels=classifications,
64
+ score=fts.score,
65
+ key=fts.rid,
66
+ score_type=ScoreType.BM25,
67
+ )
68
+
69
+ if self.inner_search_results.sentences is not None:
70
+ for sentence in self.inner_search_results.sentences.results:
71
+ resource = self.client.get_resource(sentence.rid)
72
+ if sentence.field_type == "t":
73
+ text = resource.data.texts[sentence.field].value.body
74
+ classifications = [
75
+ classification.label
76
+ for classification in resource.usermetadata.classifications
77
+ ]
78
+ yield SearchResource(
79
+ text=text,
80
+ labels=classifications,
81
+ score=sentence.score,
82
+ key=sentence.rid,
83
+ score_type=ScoreType.COSINE,
84
+ )
85
+
86
+ @property
87
+ def fulltext(self):
88
+ return self.inner_search_results.fulltext
89
+
90
+ @property
91
+ def sentences(self):
92
+ return self.inner_search_results.sentences
93
+
94
+ @property
95
+ def paragraphs(self):
96
+ return self.inner_search_results.paragraphs
97
+
98
+ @property
99
+ def resources(self):
100
+ return self.inner_search_results.resources
101
+
102
+ @property
103
+ def relations(self):
104
+ return self.inner_search_results.relations
@@ -0,0 +1,18 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,22 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ #
20
+ pytest_plugins = [
21
+ "nucliadb_sdk.tests.fixtures",
22
+ ]
@@ -0,0 +1,93 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ #
20
+ import os
21
+ from uuid import uuid4
22
+
23
+ import pytest
24
+ import requests
25
+ from pytest_docker_fixtures import images # type: ignore
26
+ from pytest_docker_fixtures.containers._base import BaseImage # type: ignore
27
+
28
+ from nucliadb_models.resource import KnowledgeBoxObj
29
+ from nucliadb_sdk.client import Environment, NucliaDBClient
30
+ from nucliadb_sdk.knowledgebox import KnowledgeBox
31
+
32
+ images.settings["nucliadb"] = {
33
+ "image": "nuclia/nucliadb",
34
+ "version": "latest",
35
+ "env": {
36
+ "NUCLIADB_DISABLE_TELEMETRY": "True",
37
+ "max_receive_message_length": "40",
38
+ },
39
+ }
40
+
41
+
42
+ class NucliaDB(BaseImage):
43
+ name = "nucliadb"
44
+ port = 8080
45
+
46
+ def check(self):
47
+ try:
48
+ response = requests.get(f"http://{self.host}:{self.get_port()}")
49
+ return response.status_code == 200
50
+ except Exception:
51
+ return False
52
+
53
+
54
+ @pytest.fixture(scope="session")
55
+ def nucliadb():
56
+ if os.environ.get("TEST_LOCAL_NUCLIADB"):
57
+ yield os.environ.get("TEST_LOCAL_NUCLIADB")
58
+ else:
59
+ container = NucliaDB()
60
+ host, port = container.run()
61
+ public_api_url = f"http://{host}:{port}"
62
+ yield public_api_url
63
+ container.stop()
64
+
65
+
66
+ @pytest.fixture(scope="function")
67
+ def knowledgebox(nucliadb):
68
+ kbslug = uuid4().hex
69
+ api_path = f"{nucliadb}/api/v1"
70
+ response = requests.post(
71
+ f"{api_path}/kbs",
72
+ json={"slug": kbslug},
73
+ headers={"X-NUCLIADB-ROLES": "MANAGER"},
74
+ )
75
+ assert response.status_code == 201
76
+
77
+ kb = KnowledgeBoxObj.parse_raw(response.content)
78
+ kbid = kb.uuid
79
+
80
+ url = f"{api_path}/kb/{kbid}"
81
+ client = NucliaDBClient(
82
+ environment=Environment.OSS,
83
+ writer_host=url,
84
+ reader_host=url,
85
+ search_host=url,
86
+ train_host=url,
87
+ )
88
+ yield KnowledgeBox(client)
89
+
90
+ response = requests.delete(
91
+ f"{api_path}/kb/{kbid}", headers={"X-NUCLIADB-ROLES": f"MANAGER"}
92
+ )
93
+ assert response.status_code == 200
@@ -0,0 +1,29 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ import pytest
21
+
22
+ from nucliadb_sdk.utils import KnowledgeBoxAlreadyExists, create_knowledge_box
23
+
24
+
25
+ def test_create_kb(nucliadb: str):
26
+ kbid = create_knowledge_box(slug="hola", nucliadb_base_url=nucliadb)
27
+ assert kbid is not None
28
+ with pytest.raises(KnowledgeBoxAlreadyExists):
29
+ kbid = create_knowledge_box(slug="hola", nucliadb_base_url=nucliadb)
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2021 Bosutech XXI S.L.
2
+ #
3
+ # nucliadb is offered under the AGPL v3.0 and as commercial software.
4
+ # For commercial licensing, contact us at info@nuclia.com.
5
+ #
6
+ # AGPL:
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Affero General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Affero General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Affero General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ from nucliadb_sdk.entities import Entity
21
+ from nucliadb_sdk.file import File
22
+ from nucliadb_sdk.knowledgebox import KnowledgeBox
23
+ from nucliadb_sdk.vectors import Vector
24
+
25
+
26
+ def test_dict_resource(knowledgebox: KnowledgeBox):
27
+ assert knowledgebox.get("mykey1") is None
28
+
29
+ knowledgebox.new_vectorset("base", 2)
30
+
31
+ resource_id = knowledgebox.upload(
32
+ key="mykey1",
33
+ binary=File(data=b"asd", filename="data"),
34
+ text="I'm Ramon",
35
+ labels=["labelset/positive"],
36
+ entities=[Entity(type="NAME", value="Ramon", positions=[(5, 9)])],
37
+ vectors=[Vector(value=[1.0, 0.2], vectorset="base")],
38
+ )
39
+ resource2 = knowledgebox[resource_id]
40
+
41
+ knowledgebox["mykey2"] = resource2
42
+
43
+ assert len(knowledgebox) == 2
44
+
45
+ del knowledgebox["mykey1"]
46
+
47
+ assert len(knowledgebox) == 1
48
+
49
+ del knowledgebox["mykey2"]
50
+
51
+ assert len(knowledgebox) == 0