BerryDbNew 1.6.10__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.
File without changes
@@ -0,0 +1,677 @@
1
+ import json
2
+ import logging
3
+ from typing import List
4
+
5
+
6
+ from constants import constants as bdb_constants
7
+ from model_garden.annotations_config import AnnotationsConfig
8
+ from model_garden.model import Model
9
+ from utils.utils import Utils
10
+ import requests # noqa: F401 (kept so existing mock.patch("<module>.requests.post") targets resolve)
11
+ from utils import http_client
12
+ from utils.redaction import log_debug, redact, register_secret
13
+ from database.database import Database
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class AnnotationProject:
18
+ __berrydb_api_key: str
19
+ __annotations_api_key: str
20
+ __project_id: int
21
+ __project_name: str
22
+
23
+ def __init__(self, berrydb_api_key:str, annotations_api_key: str, project_id: int, project_name: str):
24
+ if berrydb_api_key is None:
25
+ Utils.print_error_and_exit("BerryDB API key cannot be None")
26
+ if annotations_api_key is None:
27
+ Utils.print_error_and_exit("Annotations API key cannot be None")
28
+ if project_id is None:
29
+ Utils.print_error_and_exit("Project not found")
30
+ if project_name is None:
31
+ Utils.print_error_and_exit("Project name cannot be None")
32
+
33
+ self.__berrydb_api_key = berrydb_api_key
34
+ self.__annotations_api_key = annotations_api_key
35
+ self.__project_id = project_id
36
+ self.__project_name = project_name
37
+ register_secret(berrydb_api_key)
38
+ register_secret(annotations_api_key)
39
+
40
+ def __repr__(self) -> str:
41
+ return (f"AnnotationProject(project_id={self.__project_id!r}, project_name={self.__project_name!r}, "
42
+ f"berrydb_api_key='***', annotations_api_key='***')")
43
+
44
+ __str__ = __repr__
45
+
46
+ def berrydb_api_key(self):
47
+ """
48
+ Retrieves the BerryDB API key associated with this annotation project.
49
+
50
+ This API key is used for authenticating operations related to BerryDB
51
+ services, such as populating the project with data from a BerryDB database.
52
+
53
+ Returns:
54
+ - `str`: The BerryDB API key for the project.
55
+
56
+ Example:
57
+ ```python
58
+ # Assuming 'my_annotation_project' is an instance of AnnotationProject
59
+ # project_api_key = my_annotation_project.berrydb_api_key()
60
+ # print(f"The BerryDB API key for this project is: {project_api_key}")
61
+
62
+ # This key might be used internally or for other SDK operations
63
+ # that require authentication with the main BerryDB services.
64
+ ```
65
+ ---
66
+ """
67
+ return self.__berrydb_api_key
68
+
69
+ def annotations_api_key(self):
70
+ return self.__annotations_api_key
71
+
72
+ def project_name(self):
73
+ return self.__project_name
74
+
75
+ def project_id(self):
76
+ return self.__project_id
77
+
78
+ def setup_label_config(self, label_config):
79
+ """
80
+ The `setup_label_config` method configures the label settings for the specified annotation project. This configuration defines the labeling structure and rules to ensure consistent and organized annotation of data within the project. It is essential for tasks such as Named Entity Recognition (NER), image or text classification, and other annotation-based workflows where specific labels are applied to different data points. Without this setup, annotations and predictions added to the project will not be visible in the UI, making it a critical step for visualizing and managing the labeled data.
81
+
82
+ **Parameters**:
83
+ - **label_config** (`str`): A string representing the label configuration. This configuration defines the labels that will be used within the project and their respective properties. The format typically follows a predefined structure that outlines label categories, attributes, and potentially hierarchical relationships between labels.
84
+
85
+ **Returns**:
86
+ - `Dict`: A dict with a message indicating whether the label configuration was successfully applied to the project. In case of an error (e.g., invalid project ID or configuration format), the returned message will provide details about the failure.
87
+
88
+ **Example**
89
+ ```python
90
+ # Define your API key and other required parameters
91
+ project_id = 12345 # ID of the project you want to configure
92
+
93
+ # Label config for Topic Modeling
94
+ project_config = '''
95
+ <View>
96
+ <Text name="text" value="$content.text"/>
97
+ <View style="box-shadow: 2px 2px 5px #999; padding: 20px; margin-top: 2em; border-radius: 5px;">
98
+ <Header value="Choose text sentiment"/>
99
+ <Choices name="sentiment" toName="text" choice="single" showInLine="true">
100
+ <Choice value="Introduction"/>
101
+ <Choice value="Plan Information"/>
102
+ <Choice value="Eligibility"/>
103
+ <Choice value="Benefits"/>
104
+ <Choice value="Financial Information"/>
105
+ <Choice value="Provider Information"/>
106
+ <Choice value="Claims and Reimbursements"/>
107
+ <Choice value="Administrative Information"/>
108
+ <Choice value="Legal and Regulatory Information"/>
109
+ <Choice value="Dates and Deadlines"/>
110
+ </Choices>
111
+ </View>
112
+ </View>
113
+ '''
114
+
115
+ # Assume 'my_annotation_project' is an instance of AnnotationProject
116
+ # Setup label for Topic Modeling
117
+ label_config = my_annotation_project.setup_label_config(project_config)
118
+ if label_config:
119
+ print("Label config setup succesful!")
120
+ ```
121
+ ---
122
+ """
123
+
124
+ url = bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.setup_label_config_url.format(self.__project_id)
125
+ payload = json.dumps({"label_config": label_config})
126
+ headers = {
127
+ "Content-Type": "application/json",
128
+ "Authorization": f"Token {self.__annotations_api_key}",
129
+ }
130
+
131
+ if bdb_constants.debug_mode:
132
+ log_debug(logger, "url:", url)
133
+ log_debug(logger, "payload:", payload)
134
+
135
+ try:
136
+ response = http_client.patch(url, data=payload, headers=headers)
137
+ if response.status_code != 200:
138
+ print("Project label config setup Failed!")
139
+ Utils.handleApiCallFailure(response.json(), response.status_code)
140
+ if bdb_constants.debug_mode:
141
+ log_debug(logger, "Setup config result ", response.json())
142
+ print("Project label config setup successful!")
143
+ return response.json()
144
+ except Exception as e:
145
+ print(f"Failed to setup config: {redact(e)}")
146
+ return None
147
+
148
+ def populate(self, database:Database|str):
149
+ """
150
+ The `populate` method is designed to fill a specific annotation project with data retrieved from a designated database. This process allows users to efficiently import existing data for annotation tasks, enabling faster project setup and reducing the need for manual data entry.
151
+
152
+ **Parameters**:
153
+ - **database** (`Database | str`): The BerryDB database from which data will be sourced. This can be an instance of the `Database` class or a string representing the name of the database. If a string is provided, the SDK will attempt to connect to this database using the `berrydb_api_key` associated with this `AnnotationProject`.
154
+
155
+ **Returns**:
156
+ - `Dict | None`: A dictionary containing the API response upon successful population, or `None` if an error occurs during the process. The dictionary typically includes details about the import task.
157
+
158
+ **Example**
159
+ ```python
160
+ # Define your required parameters
161
+ database_name = "PatientDB" # Name of the database from which to populate data
162
+
163
+ # Assume 'my_annotation_project' is an instance of AnnotationProject
164
+ # Option 1: Using database name (string)
165
+ result_from_name = my_annotation_project.populate(database_name)
166
+ if result_from_name:
167
+ print(f"Successfully populated project using database name: {database_name}")
168
+
169
+ # Option 2: Using a Database object (When you either create or connect to a database)
170
+ from berrydb import BerryDB
171
+ my_database_object = BerryDB.connect(api_key="your_api_key", database_name=database_name)
172
+ result_from_object = my_annotation_project.populate(my_database_object)
173
+ ```
174
+ ---
175
+ """
176
+ if database is None or not (isinstance(database, Database) or isinstance(database, str)):
177
+ raise ValueError("Error: database is required, it should either be a string or an instance of Database")
178
+
179
+ if isinstance(database, Database):
180
+ database_name = database.database_name()
181
+ else:
182
+ database_name = database
183
+ from berrydb import BerryDB
184
+ BerryDB.connect(self.berrydb_api_key(), database_name)
185
+
186
+ if database_name is None or not isinstance(database_name, str) or database_name.strip() == "":
187
+ raise ValueError("Error: database_name is required and must be of type str")
188
+
189
+ try:
190
+ reimport_url = bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.reimport_label_studio_project_url.format(self.__project_id)
191
+ headers = {
192
+ "Content-Type": "application/json",
193
+ "Authorization": "Token {}".format(self.__annotations_api_key),
194
+ }
195
+ reimport_data = {
196
+ "file_upload_ids": [],
197
+ "files_as_tasks_list": False,
198
+ "database_name": database_name,
199
+ "bdb_api_key": self.__berrydb_api_key,
200
+ }
201
+
202
+ if bdb_constants.debug_mode:
203
+ log_debug(logger, "url:", reimport_url)
204
+ log_debug(logger, "payload:", reimport_data)
205
+
206
+ reimport_response = http_client.post(
207
+ reimport_url, data=json.dumps(reimport_data), headers=headers
208
+ )
209
+ if reimport_response.status_code != 201:
210
+ print("Project populated Failed!")
211
+ Utils.handleApiCallFailure(
212
+ reimport_response.json(), reimport_response.status_code
213
+ )
214
+ if bdb_constants.debug_mode:
215
+ log_debug(logger, "Populate project result: ", reimport_response.json())
216
+ print("Project populated successfully!")
217
+ return reimport_response.json()
218
+ except Exception as e:
219
+ print(f"Failed to populate your project: {redact(e)}")
220
+ if bdb_constants.debug_mode:
221
+ raise e
222
+ return None
223
+
224
+ def connect_to_ml(self, model:Model|None = None, model_url:str|None = None, model_title:str = "ML Model"):
225
+ """
226
+ The `connect_to_ml` method establishes a connection between a specified annotation project and a Machine Learning (ML) model backend. This integration enables the project to leverage the capabilities of the ML model for tasks such as predictions, automated annotations, or data analysis, thereby enhancing the annotation workflow.
227
+
228
+ .. tip::
229
+ You can find your BerryDB ML Model URLs for **ml_url** `here <https://app.berrydb.io/ml-models>`_.
230
+ .. #end
231
+
232
+ **Parameters**:
233
+ - **ml_url** (`str`): The URL of the ML backend to which the project will be connected. This URL should point to a deployed ML model or service that is accessible from the BerryDB environment.
234
+ - **ml_title** (`str`, optional): A title for the connection, which provides context for the integration. By default, this is set to "ML Model", but you can customize it to better describe the specific ML model being used.
235
+
236
+
237
+ **Returns**:
238
+ - `None`: This method does not return a value. Instead, it establishes the connection and prints a success or failure message indicating the result of the operation.
239
+
240
+ **Example**
241
+ ```python
242
+ ml_url = "http://app.berrydb.io/berrydb/model/model-id"
243
+ ml_title = "Text Classification Model"
244
+
245
+ # Assume 'my_annotation_project' is an instance of AnnotationProject
246
+ # Connect the annotation project to the specified ML model
247
+ my_annotation_project.connect_to_ml(ml_url, ml_title)
248
+ ```
249
+ ---
250
+ """
251
+
252
+ if model is None and model_url is None:
253
+ raise ValueError("Either `model` or `model_url` must be provided.")
254
+
255
+ if model is not None and not (isinstance(model, Model) and model.config._project_url and isinstance(model.config._project_url, str)):
256
+ raise TypeError("`model` must be an instance of Model.")
257
+
258
+ if model_url is not None and not isinstance(model_url, str):
259
+ raise TypeError("`model_url` must be a string.")
260
+
261
+ # TODO: Check for existing connected ML models and use the PATCH method to update the existing ML model connection
262
+ try:
263
+ headers = {
264
+ "Content-Type": "application/json",
265
+ "Authorization": "Token {}".format(self.__annotations_api_key),
266
+ }
267
+ ml_payload = {
268
+ "project": self.__project_id,
269
+ "title": model_title or "ML Model",
270
+ "url": model_url or model.config._project_url,
271
+ "is_interactive": False,
272
+ }
273
+ ml_connect_response = http_client.post(
274
+ bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.connect_project_to_ml_url, data=json.dumps(ml_payload), headers=headers
275
+ )
276
+ if ml_connect_response.status_code != 201:
277
+ print("Project Failed to connect to ML Model")
278
+ Utils.handleApiCallFailure(
279
+ ml_connect_response.json(), ml_connect_response.status_code
280
+ )
281
+ if bdb_constants.debug_mode:
282
+ log_debug(logger, "Connect project to ML result: ", ml_connect_response.json())
283
+ print("Project connected to ML Model successfully!")
284
+ except Exception as e:
285
+ print(f"Failed to Connect project to BerryDB ML backend: {redact(e)}")
286
+ return None
287
+
288
+ def retrieve_prediction(self, task_ids: List[int]):
289
+ """
290
+ The `retrieve_prediction` method is used to add a custom prediction to a specific task/record within an annotation project. This allows users to associate automated insights or model outputs with individual task, facilitating the annotation process and improving overall data management.
291
+
292
+ **Parameters**:
293
+ - **task_ids** (`List[int]`): The identifier of the tasks for which the prediction will be retrieved. This IDs should correspond to a valid tasks within the specified project.
294
+
295
+ **Returns**:
296
+ - `None`: This method does not return a value. Instead, it retrieves predictions to the specified tasks asynchronously.
297
+
298
+ **Example**
299
+ ```python
300
+ task_ids = [12340, 12341, 12342]
301
+
302
+ # Retrieve predictions for the specified tasks
303
+ ner_proj.retrieve_prediction(task_id)
304
+ ```
305
+ ---
306
+ """
307
+
308
+ try:
309
+ if not self.__annotations_api_key:
310
+ raise ValueError("Error: annotations_api_key is required and must be of type str")
311
+ if not self.__project_id:
312
+ raise ValueError("Error: project_id is required and must be of type int")
313
+ if not (task_ids and isinstance(task_ids, list) and len(task_ids)):
314
+ raise ValueError("Error: task_id is required and must be of type int")
315
+
316
+ url = bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.retrieve_predictions_url
317
+
318
+ payload = {
319
+ "selectedItems": {
320
+ "all": False,
321
+ "included": task_ids
322
+ },
323
+ }
324
+ headers = {
325
+ "Content-Type": "application/json",
326
+ "Authorization": f"Token {self.__annotations_api_key}",
327
+ }
328
+ params = {
329
+ "id": "retrieve_tasks_predictions",
330
+ "project": self.__project_id,
331
+ }
332
+
333
+ if bdb_constants.debug_mode:
334
+ log_debug(logger, "url:", url)
335
+ log_debug(logger, "payload:", payload)
336
+ log_debug(logger, "params:", params)
337
+
338
+ print(f'Retrieving predictions for tasks with IDs: {",".join(task_ids)} in project with ID: {self.__project_id}')
339
+ response = http_client.post(url, json=payload, headers=headers, verify=False, params=params)
340
+ if not (response.status_code == 200 or response.status_code == 201):
341
+ print(f'Failed to retrieve predictions for tasks with IDs: {",".join(task_ids)}')
342
+ Utils.handleApiCallFailure(response.json(), response.status_code)
343
+ if bdb_constants.debug_mode:
344
+ log_debug(logger, "Setup config result ", response.json())
345
+ print(f'Attempting to retrieve predictions for tasks with IDs: {",".join(task_ids)}')
346
+ return response.json()
347
+ except Exception as e:
348
+ print(f"Failed to retrieve predictions: {redact(e)}")
349
+ return None
350
+
351
+ def create_prediction(self, task_id, prediction):
352
+ """
353
+ The `create_prediction` method is used to add a custom prediction to a specific task/record within an annotation project. This allows users to associate automated insights or model outputs with individual task, facilitating the annotation process and improving overall data management.
354
+
355
+ **Parameters**:
356
+ - **task_id** (`int`): The unique identifier of the task to which the prediction will be added. This ID should correspond to a valid task within the specified project.
357
+ - **prediction** (`dict`): A dictionary containing the prediction data to be associated with the task/record. The structure of this dictionary should align with the expected format for predictions in your project based on the setup configuration, and it may include fields such as labels, polygon_labels, confidence scores, etc., and additional metadata.
358
+
359
+ **Returns**:
360
+ - `None`: This method does not return a value. Instead, it adds the prediction to the specified task and prints a success or failure message to indicate the result of the
361
+
362
+ **Example**
363
+ ```python
364
+ task_id = 67890
365
+ prediction = {
366
+ "label": "Positive",
367
+ "confidence": 0.95,
368
+ "additional_info": "Predicted based on sentiment analysis model."
369
+ }
370
+
371
+ # Assume 'my_annotation_project' is an instance of AnnotationProject
372
+ # Create a prediction for the specified task
373
+ my_annotation_project.create_prediction(task_id, prediction)
374
+ ```
375
+ ---
376
+ """
377
+
378
+ try:
379
+ import warnings
380
+
381
+ from urllib3.exceptions import InsecureRequestWarning
382
+
383
+ # Suppress only the InsecureRequestWarning from urllib3
384
+ warnings.simplefilter('ignore', InsecureRequestWarning)
385
+ if not self.__annotations_api_key:
386
+ print("Error: annotations_api_key is required and must be of type str")
387
+ return
388
+ if not self.__project_id:
389
+ print("Error: project_id is required and must be of type int")
390
+ return
391
+ if not task_id:
392
+ print("Error: task_id is required and must be of type int")
393
+ return
394
+
395
+ url = bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.create_predictions_url.format(task_id)
396
+
397
+ if type(prediction) != dict:
398
+ print("Error: prediction has to be dict type")
399
+ return
400
+ prediction["project"] = self.__project_id
401
+ prediction["task"] = task_id
402
+
403
+ payload = json.dumps(prediction)
404
+ headers = {
405
+ "Content-Type": "application/json",
406
+ "Authorization": f"Token {self.__annotations_api_key}",
407
+ }
408
+
409
+ if bdb_constants.debug_mode:
410
+ log_debug(logger, "url:", url)
411
+ log_debug(logger, "payload:", payload)
412
+
413
+ print(f"Adding prediction to task with ID: {task_id} in project with ID: {self.__project_id}")
414
+ response = http_client.post(url, data=payload, headers=headers, verify=False)
415
+ if response.status_code != 201:
416
+ print(f"Failed to add prediction to task with ID: {task_id}")
417
+ Utils.handleApiCallFailure(response.json(), response.status_code)
418
+ if bdb_constants.debug_mode:
419
+ log_debug(logger, "Setup config result ", response.json())
420
+ print(f"Successfully added prediction to task with ID: {task_id}")
421
+ return response.json()
422
+ except Exception as e:
423
+ print(f"Failed to create prediction: {redact(e)}")
424
+ return None
425
+
426
+ def create_annotation(self, task_id, annotation):
427
+ """
428
+ Create an annotation for a task in the annotation project.
429
+
430
+ **Parameters**:
431
+ - **task_id** (`int`): The unique identifier of the task that the annotation will be associated with. This ID must match an existing task within the specified project.
432
+ - **annotation** (`Dict`): A dictionary containing the details of the annotation to be added to the task. The structure of this dictionary should reflect the required fields and values for your annotation, such as labels, coordinates for bounding boxes, or any relevant metadata.
433
+
434
+ **Returns**:
435
+ - `None`: This method does not return a value. Instead, it adds the annotation to the specified task and prints a success or failure message indicating the result of the operation.
436
+
437
+ **Example**
438
+ ```python
439
+ task_id = 67890
440
+ annotation = {
441
+ "label": "Cat",
442
+ "bounding_box": {
443
+ "x": 50,
444
+ "y": 30,
445
+ "width": 100,
446
+ "height": 80
447
+ },
448
+ "confidence": 0.97
449
+ }
450
+
451
+ # Assume 'my_annotation_project' is an instance of AnnotationProject
452
+ # Create an annotation for the specified task
453
+ my_annotation_project.create_annotation(task_id, annotation)
454
+ ```
455
+ ---
456
+ """
457
+
458
+ try:
459
+ import warnings
460
+
461
+ from urllib3.exceptions import InsecureRequestWarning
462
+
463
+ # Suppress only the InsecureRequestWarning from urllib3
464
+ warnings.simplefilter('ignore', InsecureRequestWarning)
465
+ if not self.__annotations_api_key:
466
+ raise ValueError("Error: annotations_api_key is required and must be of type str")
467
+ if self.__project_id is None:
468
+ raise ValueError("Error: project_id is required and must be of type int")
469
+ if not self.__berrydb_api_key:
470
+ raise ValueError("Error: berrydb_api_key is required")
471
+ if task_id is None:
472
+ raise ValueError("Error: task_id is required and must be of type int")
473
+ if not annotation:
474
+ raise ValueError("Error: annotation is required")
475
+
476
+ url = bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.create_annotations_url.format(task_id, self.__project_id, self.__berrydb_api_key)
477
+
478
+ if type(annotation) != dict:
479
+ raise ValueError("Error: annotations has to be dict type")
480
+ annotation["project"] = self.__project_id
481
+
482
+ payload = json.dumps(annotation)
483
+ headers = {
484
+ "Content-Type": "application/json",
485
+ "Authorization": f"Token {self.__annotations_api_key}",
486
+ }
487
+
488
+ if bdb_constants.debug_mode:
489
+ log_debug(logger, "url:", url)
490
+ log_debug(logger, "payload:", payload)
491
+
492
+ print(f"Adding annotation to task with ID: {task_id} in project with ID: {self.__project_id}")
493
+ response = http_client.post(url, data=payload, headers=headers, verify=False)
494
+ if response.status_code != 201:
495
+ print(f"Failed to add annotation to task with ID: {task_id}")
496
+ Utils.handleApiCallFailure(response.json(), response.status_code)
497
+ if bdb_constants.debug_mode:
498
+ log_debug(logger, "Setup config result ", response.json())
499
+ print(f"Successfully added annotation to task with ID: {task_id}")
500
+ return response.json()
501
+ except Exception as e:
502
+ print(f"Failed to create annotations: {redact(e)}")
503
+ return None
504
+
505
+ def attach_annotations_config(self, annotations_config: AnnotationsConfig):
506
+ """
507
+ Attaches a predefined annotations configuration to the annotation project.
508
+
509
+ This method links an `AnnotationsConfig` instance to the current project.
510
+ The `AnnotationsConfig` defines how annotations should be generated,
511
+ potentially using LLMs, specific prompts, and data transformations.
512
+ Attaching it to a project enables automated annotation workflows based
513
+ on that configuration.
514
+
515
+ .. note::
516
+ The `annotations_config` parameter must be an instance of
517
+ :class:`~berrydb.model_garden.annotations_config.AnnotationsConfig`.
518
+ Please refer to the :doc:`annotation_config` page for details on how to
519
+ create and save an ``AnnotationsConfig`` object using its builder.
520
+
521
+ Parameters:
522
+ - **annotations_config** (`AnnotationsConfig`): An instance of `AnnotationsConfig`
523
+ that has been previously created and saved. The `name` attribute of this
524
+ config is used to identify it in BerryDB.
525
+
526
+ Returns:
527
+ - `dict`: A dictionary containing the response from BerryDB, typically
528
+ confirming that the configuration has been successfully attached.
529
+
530
+ Raises:
531
+ - `ValueError`: If the `annotations_config` does not have a `name`.
532
+
533
+ Example:
534
+ ```python
535
+ from berrydb import AnnotationsConfig
536
+
537
+ # Assume 'my_annotation_project' is an instance of AnnotationProject
538
+ # and 'berrydb_api_key' is your BerryDB API key.
539
+
540
+ # First, create and save an AnnotationsConfig
541
+ ner_config = (
542
+ AnnotationsConfig.builder()
543
+ .name("my-project-ner-config")
544
+ .input_transform_expression("data.text_content")
545
+ .output_transform_expression("annotations.ner_tags")
546
+ .llm_provider("openai")
547
+ .llm_model("gpt-4o-mini")
548
+ .prompt("Extract named entities: {{input}}")
549
+ .build()
550
+ )
551
+ ner_config.save(berrydb_api_key) # Save it to BerryDB
552
+
553
+ # Now, attach this saved config to your annotation project
554
+ try:
555
+ my_annotation_project.attach_annotations_config(ner_config)
556
+ print(f"Successfully attached '{ner_config.name}' to project '{my_annotation_project.project_name()}'.")
557
+ except Exception as e:
558
+ print(f"Error attaching annotations config: {redact(e)}")
559
+ ```
560
+ ---
561
+ """
562
+ if not annotations_config.name:
563
+ raise ValueError("Error: annotations_config name is required and must be of type string")
564
+
565
+ url = bdb_constants.LABEL_STUDIO_BASE_URL + bdb_constants.attach_annotations_config_to_project_url.format(self.__project_id)
566
+ payload = json.dumps({"annotation_config_name": annotations_config.name})
567
+ headers = {
568
+ "Content-Type": "application/json",
569
+ "Authorization": f"Token {self.__annotations_api_key}",
570
+ }
571
+
572
+ if bdb_constants.debug_mode:
573
+ log_debug(logger, "url:", url)
574
+ log_debug(logger, "payload:", payload)
575
+
576
+ try:
577
+ response = http_client.patch(url, data=payload, headers=headers)
578
+ if response.status_code != 200:
579
+ print("Project label config setup Failed!")
580
+ Utils.handleApiCallFailure(response.json(), response.status_code)
581
+ if bdb_constants.debug_mode:
582
+ log_debug(logger, "Setup config result ", response.json())
583
+ print("Project label config setup successful!")
584
+ return response.json()
585
+ except Exception as e:
586
+ print(f"Failed to setup config: {redact(e)}")
587
+ return None
588
+
589
+ def get_task_data(self):
590
+ """
591
+ Retrieves a list of all tasks within the annotation project, along with
592
+ their corresponding BerryDB document IDs and database names.
593
+
594
+ This method fetches information for every task in the project. Each task
595
+ typically represents a single data item
596
+ that has been imported into the annotation project. The `task_id` returned
597
+ for each task can then be used with other methods like
598
+ :meth:`retrieve_prediction <annotation_project.annotation_project.AnnotationProject.retrieve_prediction>`,
599
+ :meth:`create_annotation <annotation_project.annotation_project.AnnotationProject.create_annotation>`, or
600
+ :meth:`create_prediction <annotation_project.annotation_project.AnnotationProject.create_prediction>`
601
+ to interact with individual tasks.
602
+
603
+ Returns:
604
+ - `List[Dict[str, any]]`: A list of dictionaries, where each dictionary
605
+ represents a task and contains the following keys:
606
+ - **'task_id'** (`int`): The unique identifier of the task within the
607
+ annotation project.
608
+ - **'document_id'** (`str`): The original identifier of the document
609
+ in BerryDB from which this task was created.
610
+ - **'database_name'** (`str`): The name of the BerryDB database from
611
+ which the original document was sourced.
612
+
613
+ Raises:
614
+ - `Exception`: If there's an issue fetching the tasks from BerryDB,
615
+ for example, due to network issues or if the project is not found.
616
+
617
+ Example:
618
+ ```python
619
+ # Assuming 'my_annotation_project' is an instance of AnnotationProject
620
+
621
+ try:
622
+ all_tasks_info = my_annotation_project.get_task_data()
623
+ if all_tasks_info:
624
+ print(f"Found {len(all_tasks_info)} tasks in project '{my_annotation_project.project_name()}':")
625
+ for task_info in all_tasks_info:
626
+ task_id = task_info['task_id']
627
+
628
+ Example: Retrieve prediction for this task
629
+ try:
630
+ prediction = my_annotation_project.retrieve_prediction(task_ids=[task_id])
631
+ if prediction:
632
+ print(f" Prediction for task {task_id}: {prediction}")
633
+ except Exception as pred_e:
634
+ print(f" Could not retrieve prediction for task {task_id}: {pred_e}")
635
+
636
+ # Example: Create an example annotation for this task
637
+ ex_annotation_data = {
638
+ "result": [
639
+ {
640
+ "from_name": "label", # Matches your label config
641
+ "to_name": "text", # Matches your label config
642
+ "type": "choices", # Matches your label config
643
+ "value": {"choices": ["SomeLabel"]}
644
+ }
645
+ ]
646
+ }
647
+ try:
648
+ my_annotation_project.create_annotation(task_id, ex_annotation_data)
649
+ print(f" Successfully created annotation for task {task_id}")
650
+ except Exception as ann_e:
651
+ print(f" Could not create annotation for task {task_id}: {ann_e}")
652
+
653
+ else:
654
+ print(f"No tasks found in project '{my_annotation_project.project_name()}'.")
655
+ except Exception as e:
656
+ print(f"Error retrieving task data: {redact(e)}")
657
+ ```
658
+ """
659
+ url = f"{bdb_constants.LABEL_STUDIO_BASE_URL}/api/projects/{self.__project_id}/tasks"
660
+ headers = {"Authorization": f"Token {self.__annotations_api_key}"}
661
+ params = {"page": 1, "page_size": -1}
662
+
663
+ task_ids = []
664
+
665
+ try:
666
+ response = http_client.get(url, headers=headers, params=params)
667
+ if response.status_code != 200:
668
+ raise Exception(f"Failed to fetch tasks for project with name: {self.__project_name}")
669
+
670
+ data = response.json()
671
+ tasks = data or []
672
+
673
+ task_ids.extend([{"task_id": task["id"], "document_id": task.get("data", {}).get("_meta", {}).get("objectId", {}),\
674
+ "database_name": task.get("data", {}).get("_meta", {}).get("databaseName", {})} for task in tasks])
675
+ return task_ids
676
+ except:
677
+ raise Exception(f"Failed to fetch tasks for project with name: {self.__project_name}")