indico_toolkit 6.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. indico_toolkit/__init__.py +24 -0
  2. indico_toolkit/association/__init__.py +15 -0
  3. indico_toolkit/association/association.py +65 -0
  4. indico_toolkit/association/extracted_tokens.py +106 -0
  5. indico_toolkit/association/line_items.py +189 -0
  6. indico_toolkit/association/positioning.py +301 -0
  7. indico_toolkit/association/splitting.py +41 -0
  8. indico_toolkit/auto_populate/__init__.py +3 -0
  9. indico_toolkit/auto_populate/populator.py +362 -0
  10. indico_toolkit/auto_populate/types.py +66 -0
  11. indico_toolkit/auto_review/__init__.py +6 -0
  12. indico_toolkit/auto_review/auto_review_functions.py +123 -0
  13. indico_toolkit/auto_review/auto_reviewer.py +62 -0
  14. indico_toolkit/client.py +35 -0
  15. indico_toolkit/errors.py +32 -0
  16. indico_toolkit/etloutput/__init__.py +210 -0
  17. indico_toolkit/etloutput/cell.py +47 -0
  18. indico_toolkit/etloutput/errors.py +16 -0
  19. indico_toolkit/etloutput/etloutput.py +110 -0
  20. indico_toolkit/etloutput/range.py +30 -0
  21. indico_toolkit/etloutput/table.py +44 -0
  22. indico_toolkit/etloutput/token.py +25 -0
  23. indico_toolkit/indico_wrapper/__init__.py +15 -0
  24. indico_toolkit/indico_wrapper/dataset.py +114 -0
  25. indico_toolkit/indico_wrapper/doc_extraction.py +79 -0
  26. indico_toolkit/indico_wrapper/download.py +193 -0
  27. indico_toolkit/indico_wrapper/indico_wrapper.py +119 -0
  28. indico_toolkit/indico_wrapper/modelop.py +200 -0
  29. indico_toolkit/indico_wrapper/reviewer.py +111 -0
  30. indico_toolkit/indico_wrapper/workflow.py +231 -0
  31. indico_toolkit/metrics/__init__.py +11 -0
  32. indico_toolkit/metrics/compare_ground_truth.py +123 -0
  33. indico_toolkit/metrics/compare_models.py +180 -0
  34. indico_toolkit/metrics/metrics.py +373 -0
  35. indico_toolkit/metrics/plotting.py +102 -0
  36. indico_toolkit/ocr/__init__.py +9 -0
  37. indico_toolkit/ocr/customocr_object.py +42 -0
  38. indico_toolkit/ocr/ondoc_object.py +93 -0
  39. indico_toolkit/ocr/standard_object.py +54 -0
  40. indico_toolkit/pipelines/__init__.py +3 -0
  41. indico_toolkit/pipelines/file_processing.py +157 -0
  42. indico_toolkit/polling/__init__.py +8 -0
  43. indico_toolkit/polling/autoreview.py +204 -0
  44. indico_toolkit/polling/downstream.py +145 -0
  45. indico_toolkit/polling/queries.py +68 -0
  46. indico_toolkit/py.typed +0 -0
  47. indico_toolkit/results/__init__.py +104 -0
  48. indico_toolkit/results/document.py +78 -0
  49. indico_toolkit/results/errors.py +4 -0
  50. indico_toolkit/results/model.py +60 -0
  51. indico_toolkit/results/normalization.py +159 -0
  52. indico_toolkit/results/predictionlist.py +395 -0
  53. indico_toolkit/results/predictions/__init__.py +88 -0
  54. indico_toolkit/results/predictions/box.py +58 -0
  55. indico_toolkit/results/predictions/citation.py +47 -0
  56. indico_toolkit/results/predictions/classification.py +50 -0
  57. indico_toolkit/results/predictions/documentextraction.py +160 -0
  58. indico_toolkit/results/predictions/extraction.py +35 -0
  59. indico_toolkit/results/predictions/formextraction.py +121 -0
  60. indico_toolkit/results/predictions/group.py +29 -0
  61. indico_toolkit/results/predictions/prediction.py +41 -0
  62. indico_toolkit/results/predictions/span.py +43 -0
  63. indico_toolkit/results/predictions/summarization.py +103 -0
  64. indico_toolkit/results/predictions/unbundling.py +51 -0
  65. indico_toolkit/results/result.py +152 -0
  66. indico_toolkit/results/review.py +32 -0
  67. indico_toolkit/results/utilities.py +73 -0
  68. indico_toolkit/retry.py +94 -0
  69. indico_toolkit/snapshots/__init__.py +3 -0
  70. indico_toolkit/snapshots/snapshot.py +340 -0
  71. indico_toolkit/structure/__init__.py +3 -0
  72. indico_toolkit/structure/create_structure.py +232 -0
  73. indico_toolkit/structure/queries.py +119 -0
  74. indico_toolkit/structure/utils.py +16 -0
  75. indico_toolkit/types/__init__.py +12 -0
  76. indico_toolkit/types/classification.py +92 -0
  77. indico_toolkit/types/extractions.py +227 -0
  78. indico_toolkit/types/predictions.py +28 -0
  79. indico_toolkit/types/workflow_object.py +133 -0
  80. indico_toolkit-6.14.0.dist-info/LICENSE +21 -0
  81. indico_toolkit-6.14.0.dist-info/METADATA +144 -0
  82. indico_toolkit-6.14.0.dist-info/RECORD +83 -0
  83. indico_toolkit-6.14.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,340 @@
1
+ import json
2
+ import os
3
+ from json import JSONDecodeError
4
+ from typing import List, Tuple, Union
5
+
6
+ from ..errors import ToolkitInputError, ToolkitInstantiationError
7
+
8
+ try:
9
+ import pandas as pd
10
+
11
+ _PANDAS_INSTALLED = True
12
+ except ImportError as error:
13
+ _PANDAS_INSTALLED = False
14
+ _IMPORT_ERROR = error
15
+
16
+ # TODO: add functionality for classification snapshots
17
+
18
+
19
+ class Snapshot:
20
+ def __init__(
21
+ self,
22
+ path_to_snapshot: str,
23
+ text_col: str = None,
24
+ label_col: str = None,
25
+ file_name_col: str = None,
26
+ ):
27
+ """
28
+ Combine and manipulate a Teach Task's snapshot.
29
+
30
+ Args:
31
+ path_to_snapshot (str): path to Snapshot CSV
32
+ text_col (str, optional): Column with text, will be inferred if not
33
+ provided. Defaults to None.
34
+ label_col (str, optional): Column with labels, will be inferred if not
35
+ provided. Defaults to None.
36
+ file_name_col (str, optional): Column with file names, will be inferred if
37
+ not provided. Defaults to None.
38
+ """
39
+ if not _PANDAS_INSTALLED:
40
+ raise RuntimeError(
41
+ "snapshots require additional dependencies: "
42
+ "`pip install indico-toolkit[snapshots]`"
43
+ ) from _IMPORT_ERROR
44
+
45
+ self.path_to_snapshot = path_to_snapshot
46
+ self.df: pd.DataFrame = pd.read_csv(path_to_snapshot)
47
+ self.label_col = label_col
48
+ if label_col is None:
49
+ self._infer_label_col()
50
+ self._convert_col_from_json()
51
+ self.text_col = text_col
52
+ if text_col is None:
53
+ self._infer_text_col()
54
+ self.file_name_col = file_name_col
55
+ if file_name_col is None:
56
+ self._infer_file_name_col()
57
+
58
+ def remove_extraction_labels(self, labels_to_remove: List[str]):
59
+ """
60
+ Remove unwanted labels from label column
61
+ Args:
62
+ labels_to_remove (List[str]): the labels you'd like to remove
63
+ """
64
+ updated_predictions = []
65
+ for label_set in self.df[self.label_col]:
66
+ label_set.update(
67
+ {
68
+ "targets": [
69
+ i
70
+ for i in label_set["targets"]
71
+ if i["label"] not in labels_to_remove
72
+ ]
73
+ }
74
+ )
75
+ updated_predictions.append(label_set)
76
+ self.df[self.label_col] = updated_predictions
77
+
78
+ def standardize_column_names(
79
+ self,
80
+ text_col: str = "source",
81
+ label_col: str = "target",
82
+ file_name_col: str = "file_name",
83
+ ):
84
+ """
85
+ To allow merging/appending snapshots, standardize the key column headers
86
+ """
87
+ self.df.rename(
88
+ mapper={
89
+ self.file_name_col: file_name_col,
90
+ self.text_col: text_col,
91
+ self.label_col: label_col,
92
+ },
93
+ inplace=True,
94
+ errors="raise",
95
+ axis=1,
96
+ )
97
+ self.file_name_col = file_name_col
98
+ self.label_col = label_col
99
+ self.text_col = text_col
100
+
101
+ def drop_unneeded_columns(self, columns_to_drop: List[str] = None):
102
+ """
103
+ Keep only text, labels, and file name columns or specify columns to drop by
104
+ passing them in as a list.
105
+ """
106
+ if columns_to_drop:
107
+ self.df.drop(labels=columns_to_drop, axis=1, inplace=True)
108
+ else:
109
+ self.df = self.df[[self.label_col, self.text_col, self.file_name_col]]
110
+
111
+ def append(self, snap_to_add: "Snapshot"):
112
+ """
113
+ Append the rows from another Snapshot to this snapshot. Ensure column names are
114
+ standardized beforehand.
115
+
116
+ Args:
117
+ snap_to_add (Snapshot): Snapshot to add
118
+ """
119
+ self._assert_key_column_names_match(snap_to_add)
120
+ self.df = pd.concat([self.df, snap_to_add.df], ignore_index=True)
121
+
122
+ def get_extraction_label_names(self):
123
+ """
124
+ Return a list of all labeled classes in an extraction snapshot.
125
+ """
126
+ label_column = self.df[self.label_col].tolist()
127
+ label_set = set()
128
+ for labels in label_column:
129
+ for label in labels["targets"]:
130
+ label_set.add(label["label"])
131
+ return sorted(list(label_set))
132
+
133
+ def merge_by_file_name(
134
+ self,
135
+ snap_to_merge: "Snapshot",
136
+ ensure_identical_text: bool = True,
137
+ ):
138
+ """
139
+ Merge extraction labels for identical files. Merge is 'left' and file names /
140
+ rows only present in 'snap_to_merge' are excluded.
141
+
142
+ Args:
143
+ snap_to_merge (Snapshot): Snapshot you want to merge
144
+ ensure_identical_text (bool, optional): Require document text to be
145
+ identical for common file name. Defaults to True.
146
+ """
147
+ self._assert_key_column_names_match(snap_to_merge)
148
+ suffix = "_to_merge"
149
+ merged = pd.merge(
150
+ self.df,
151
+ snap_to_merge.df,
152
+ how="left",
153
+ on=self.file_name_col,
154
+ suffixes=(None, suffix),
155
+ )
156
+ updated_labels = []
157
+ unmatched_files = []
158
+ for _, row in merged.iterrows():
159
+ to_merge_label = f"{self.label_col}{suffix}"
160
+ labels = row[self.label_col]
161
+ if isinstance(row[to_merge_label], (dict, list)):
162
+ if ensure_identical_text:
163
+ try:
164
+ assert row[self.text_col] == row[f"{self.text_col}{suffix}"]
165
+ except AssertionError:
166
+ raise ToolkitInputError(
167
+ f"Text from {row[self.file_name_col]} doesn't match"
168
+ )
169
+ labels["targets"].extend(row[to_merge_label]["targets"])
170
+ else:
171
+ unmatched_files.append(row[self.file_name_col])
172
+ updated_labels.append(labels)
173
+ if unmatched_files:
174
+ print(f"No new labels were added for these files: {unmatched_files}")
175
+ merged[self.label_col] = updated_labels
176
+ self.df = merged.drop([i for i in merged.columns if i.endswith(suffix)], axis=1)
177
+
178
+ def to_csv(
179
+ self,
180
+ path: str,
181
+ only_keep_key_columns: bool = True,
182
+ convert_label_col_to_json: bool = True,
183
+ ):
184
+
185
+ if only_keep_key_columns:
186
+ self.drop_unneeded_columns()
187
+ if convert_label_col_to_json:
188
+ self._convert_col_to_json(self.label_col)
189
+ self.df.to_csv(path, index=False)
190
+
191
+ def get_all_labeled_text(
192
+ self, label_name: str, return_per_document: bool = False
193
+ ) -> Union[List[List[str]], List[str]]:
194
+ """
195
+ Get all of the text that was tagged for a given label
196
+ Args:
197
+ label_name (str): name of the label
198
+ return_per_document (bool, optional): return a list per document or one list
199
+ with everything. Defaults to False.
200
+ """
201
+ available_labels = self.get_extraction_label_names()
202
+ if label_name not in available_labels:
203
+ raise ToolkitInputError(
204
+ f"'{label_name}' not present among available labels: {available_labels}"
205
+ )
206
+ all_labeled_text = []
207
+ for text, labels in zip(self.df[self.text_col], self.df[self.label_col]):
208
+ text_found = []
209
+ for lab in labels["targets"]:
210
+ if lab["label"] == label_name:
211
+ for span in lab["spans"]:
212
+ text_found.append(text[span["start"] : span["end"]])
213
+ if return_per_document:
214
+ all_labeled_text.append(text_found)
215
+ else:
216
+ all_labeled_text.extend(text_found)
217
+ return all_labeled_text
218
+
219
+ def split_and_write_to_csv(
220
+ self, output_dir: str, num_splits: int = 5, output_base_name: str = "split_num"
221
+ ) -> None:
222
+ """
223
+ For large files that may face memory constraints, split the file into multiple
224
+ CSVs and write to disk.
225
+
226
+ Args:
227
+ output_dir (str): Location where split files will be written.
228
+ num_splits (int, optional): The number of splits of the CSV. Defaults to 5.
229
+ output_base_name (str, optional): The base name of the split CSVs:
230
+ Defaults to "split_num". So files would be "split_num_1.csv",
231
+ "split_num_2.csv", etc.
232
+ """
233
+ split_length = self.number_of_samples // num_splits
234
+ rows_taken = 0
235
+ for i in range(1, num_splits + 1):
236
+ if i == num_splits:
237
+ sub_df = self.df.iloc[rows_taken:]
238
+ else:
239
+ sub_df = self.df.iloc[rows_taken : rows_taken + split_length]
240
+ split_file_loc = os.path.join(output_dir, f"{output_base_name}_{i}.csv")
241
+ sub_df.to_csv(split_file_loc, index=False)
242
+ rows_taken += split_length
243
+ print(f"Wrote split {i} of {num_splits}: {split_file_loc}")
244
+
245
+ def update_label_col_format(self, task_type: str = "annotation"):
246
+ """
247
+ Modifies label column to updated format with spans.
248
+ Args:
249
+ task_type (str): Task type to specifiy df as
250
+ (annotation = extraction, classification = classification)
251
+
252
+ Note: page_num in the span is currently set to None.
253
+ """
254
+ updated_column = []
255
+ for label_set in self.df[self.label_col]:
256
+ updated_targets = []
257
+ for span in label_set:
258
+ updated_span = {
259
+ "label": span["label"],
260
+ "spans": [
261
+ {"start": span["start"], "page_num": None, "end": span["end"]}
262
+ ],
263
+ }
264
+ updated_targets.append(updated_span)
265
+
266
+ updated_column.append({"task_type": task_type, "targets": updated_targets})
267
+ self.df[self.label_col] = updated_column
268
+
269
+ def __eq__(self, other: "Snapshot"):
270
+ """
271
+ Check if two snapshots can be merged based on common column names
272
+ """
273
+ return (
274
+ self.label_col == other.label_col
275
+ and self.text_col == other.text_col
276
+ and self.file_name_col == other.file_name_col
277
+ )
278
+
279
+ def _convert_col_from_json(self):
280
+ try:
281
+ self.df[self.label_col] = self.df[self.label_col].apply(json.loads)
282
+ except (TypeError, JSONDecodeError):
283
+ if isinstance(self.df[self.label_col].iloc[0], list):
284
+ return # json column already converted
285
+ raise ToolkitInputError(
286
+ f"{self.label_col} doesn't contain valid extraction labels"
287
+ )
288
+
289
+ def _assert_key_column_names_match(self, snapshot: "Snapshot"):
290
+ try:
291
+ assert self == snapshot
292
+ except AssertionError:
293
+ raise ToolkitInputError(
294
+ f"Column names don't match for this Snapshot: {snapshot}"
295
+ )
296
+
297
+ def _convert_col_to_json(self, column: str):
298
+ self.df[column] = self.df[column].apply(json.dumps)
299
+
300
+ def _infer_text_col(self):
301
+ if "text" in self.df.columns:
302
+ self.text_col = "text"
303
+ elif "document" in self.df.columns:
304
+ self.text_col = "document"
305
+ elif "source" in self.df.columns:
306
+ self.text_col = "source"
307
+ else:
308
+ error_message = self._format_column_error_message("text_col")
309
+ raise ToolkitInstantiationError(error_message)
310
+
311
+ def _infer_label_col(self):
312
+ question_col = [
313
+ col for col in self.df.columns if "question" in col or "target" in col
314
+ ]
315
+ if len(question_col) != 1:
316
+ error_message = self._format_column_error_message("label_col")
317
+ raise ToolkitInstantiationError(error_message)
318
+ self.label_col = question_col[0]
319
+
320
+ def _infer_file_name_col(self):
321
+ file_name_col = [col for col in self.df.columns if "file_name" in col]
322
+ if len(file_name_col) != 1:
323
+ error_message = self._format_column_error_message("file_name_col")
324
+ raise ToolkitInstantiationError(error_message)
325
+ self.file_name_col = file_name_col[0]
326
+
327
+ def _format_column_error_message(self, column):
328
+ column_list = list(self.df.columns)
329
+ return f"You must set attribute '{column}' from {column_list}"
330
+
331
+ def __repr__(self):
332
+ return f"Snapshot, label_col: {self.label_col}, loc: {self.path_to_snapshot}"
333
+
334
+ @property
335
+ def number_of_samples(self) -> int:
336
+ return self.df.shape[0]
337
+
338
+ @property
339
+ def shape(self) -> Tuple[int, int]:
340
+ return self.df.shape
@@ -0,0 +1,3 @@
1
+ from .create_structure import Structure
2
+
3
+ __all__ = ("Structure",)
@@ -0,0 +1,232 @@
1
+ import json
2
+ import os
3
+ import shutil
4
+ import tempfile
5
+ from typing import List
6
+
7
+ from indico.queries import (
8
+ AddModelGroupComponent,
9
+ CreateDataset,
10
+ CreateWorkflow,
11
+ GetDataset,
12
+ GetWorkflow,
13
+ NewLabelsetArguments,
14
+ )
15
+ from indico.types import (
16
+ Dataset,
17
+ OcrEngine,
18
+ TableReadOrder,
19
+ Workflow,
20
+ )
21
+
22
+ from ..errors import ToolkitInputError
23
+ from .queries import GetExampleIds, GetTeachDetails, LabelTeachTask
24
+ from .utils import ModelTaskType
25
+
26
+
27
+ class Structure:
28
+ def __init__(self, client):
29
+ self.client = client
30
+
31
+ def create_dataset(
32
+ self,
33
+ dataset_name: str,
34
+ files_to_upload: List[str],
35
+ read_api: bool = True,
36
+ **kwargs,
37
+ ) -> Dataset:
38
+ """
39
+ Creates a dataset from a list of files.
40
+
41
+ Args:
42
+ name_of_dataset (str): Name of the created dataset
43
+ file_path (str): Path of the file to copy.
44
+ read_api (bool, optional): OCR Engine used for the dataset.
45
+ Defaults to True=READ_API / False=OMNIPAGE
46
+ Kwargs:
47
+ Advanced OCR settings
48
+ """
49
+ read_api_settings = {
50
+ "auto_rotate": True,
51
+ "single_column": False,
52
+ "upscale_images": True,
53
+ "languages": ["ENG"],
54
+ }
55
+ omnipage_settings = {
56
+ **read_api_settings,
57
+ "force_render": True,
58
+ "native_layout": False,
59
+ "native_pdf": False,
60
+ "table_read_order": "row",
61
+ }
62
+ for arg in kwargs.keys():
63
+ if read_api:
64
+ read_api_settings.update({arg: kwargs[arg]})
65
+ else:
66
+ if arg == "table_read_order" and kwargs[arg] not in ["row", "column"]:
67
+ raise ToolkitInputError(
68
+ f"Keyword argument {arg} got an unexpected value of "
69
+ f"{kwargs[arg]}, expected value of either 'row' or 'column'"
70
+ )
71
+ omnipage_settings.update({arg: kwargs[arg]})
72
+
73
+ ocr_engine = OcrEngine.READAPI if read_api else OcrEngine.OMNIPAGE
74
+ if read_api:
75
+ ocr_settings = {"read_api_ocr_options": read_api_settings}
76
+ else:
77
+ omnipage_settings["table_read_order"] = (
78
+ TableReadOrder.ROW
79
+ if omnipage_settings["table_read_order"] == "row"
80
+ else TableReadOrder.COLUMN
81
+ )
82
+ ocr_settings = {"omnipage_ocr_options": omnipage_settings}
83
+
84
+ dataset = self.client.call(
85
+ CreateDataset(
86
+ dataset_name,
87
+ files=files_to_upload,
88
+ dataset_type="DOCUMENT",
89
+ ocr_engine=ocr_engine,
90
+ **ocr_settings,
91
+ )
92
+ )
93
+ print(f"Dataset created with ID: {dataset.id}")
94
+ return dataset
95
+
96
+ def create_duplicate_dataset(
97
+ self,
98
+ dataset_name: str,
99
+ file_path: str,
100
+ times_to_copy_files=55,
101
+ read_api=True,
102
+ **kwargs,
103
+ ) -> Dataset:
104
+ """
105
+ Creates a dataset w/ duplicate instances of 1 file, historically used to create
106
+ a spoofed demo.
107
+
108
+ Args:
109
+ file_path (str): Path of the file to copy.
110
+ name_of_dataset (str): Name of the created dataset
111
+ times_to_copy_files (int, optional): Amount of times to copy the file.
112
+ Defaults to 55.
113
+ read_api (bool, optional): OCR Engine used for the dataset.
114
+ Defaults to True=READ_API / False=OMNIPAGE
115
+ Kwargs:
116
+ Advanced OCR settings
117
+ """
118
+ tempdir = tempfile.TemporaryDirectory()
119
+ try:
120
+ files_to_add = []
121
+ for i in range(times_to_copy_files):
122
+ fpath = os.path.join(tempdir.name, f"sample_{i+1}.pdf")
123
+ shutil.copy(file_path, fpath)
124
+ files_to_add.append(fpath)
125
+ self.create_dataset(
126
+ dataset_name=dataset_name,
127
+ files_to_upload=files_to_add,
128
+ read_api=read_api,
129
+ **kwargs,
130
+ )
131
+ except Exception as e:
132
+ print(e)
133
+ finally:
134
+ tempdir.cleanup()
135
+
136
+ def create_workflow(self, name: str, dataset_id: int) -> Workflow:
137
+ """
138
+
139
+ Args:
140
+ name (str): Name of the workflow to be created
141
+ dataset_id (int): Dataset ID for the newly created workflow
142
+ """
143
+ workflow = self.client.call(CreateWorkflow(name=name, dataset_id=dataset_id))
144
+ print(f"Workflow created with ID: {workflow.id}")
145
+ return workflow
146
+
147
+ def add_teach_task(
148
+ self,
149
+ task_name: str,
150
+ labelset_name: str,
151
+ target_names: List[str],
152
+ dataset_id: int,
153
+ workflow_id: int,
154
+ model_type: str = "annotation",
155
+ data_column: str = "document",
156
+ prev_comp_id: int = None,
157
+ **kwargs,
158
+ ) -> Workflow:
159
+ """
160
+ Args:
161
+ task_name (str): Teach task name
162
+ labelset_name (str): Name for created labelset
163
+ target_names (List[str]): List of target (label) names
164
+ dataset_id (int): Dataset ID.
165
+ workflow_id (int): Workflow ID.
166
+ model_type (str, optional): Defaults to annotation.
167
+ Available model types:
168
+ classification
169
+ annotation
170
+ classification_unbundling
171
+ data_column (str, optional): Defaults to "document".
172
+ Kwargs:
173
+ Advanced model training options
174
+ Returns:
175
+ Updated Workflow with newly added model group component.
176
+ """
177
+ workflow = self.client.call(GetWorkflow(workflow_id))
178
+ dataset = self.client.call(GetDataset(dataset_id))
179
+ model_map = {
180
+ "classification": ModelTaskType.CLASSIFICATION,
181
+ "annotation": ModelTaskType.ANNOTATION,
182
+ "classification_unbundling": ModelTaskType.CLASSIFICATION_UNBUNDLING,
183
+ "classification_multiple": ModelTaskType.CLASSIFICATION_MULTIPLE,
184
+ }
185
+ if model_type not in model_map.keys():
186
+ raise ToolkitInputError(
187
+ f"{model_type} not found. Available options "
188
+ f"include {[model for model in model_map.keys()]}"
189
+ )
190
+ workflow = self.client.call(GetWorkflow(workflow_id))
191
+ if not prev_comp_id:
192
+ prev_comp_id = workflow.component_by_type("INPUT_OCR_EXTRACTION").id
193
+
194
+ column_id = dataset.datacolumn_by_name(data_column).id
195
+ new_labelset = NewLabelsetArguments(
196
+ labelset_name,
197
+ datacolumn_id=column_id,
198
+ task_type=model_map[model_type],
199
+ target_names=target_names,
200
+ )
201
+
202
+ workflow = self.client.call(
203
+ AddModelGroupComponent(
204
+ workflow_id=workflow.id,
205
+ name=task_name,
206
+ dataset_id=dataset.id,
207
+ source_column_id=column_id,
208
+ after_component_id=prev_comp_id,
209
+ new_labelset_args=new_labelset,
210
+ model_training_options=json.dumps(kwargs),
211
+ )
212
+ )
213
+ print(
214
+ "Newly created teach task with teach_id: "
215
+ f"{workflow.components[-1].model_group.questionnaire_id}"
216
+ )
217
+ return workflow
218
+
219
+ def get_teach_details(self, teach_task_id: int):
220
+ return self.client.call(GetTeachDetails(teach_task_id=teach_task_id))
221
+
222
+ def get_example_ids(self, model_group_id: int, limit: int):
223
+ return self.client.call(
224
+ GetExampleIds(model_group_id=model_group_id, limit=limit)
225
+ )
226
+
227
+ def label_teach_task(self, label_set_id: int, labels: dict, model_group_id: int):
228
+ return self.client.call(
229
+ LabelTeachTask(
230
+ label_set_id=label_set_id, labels=labels, model_group_id=model_group_id
231
+ )
232
+ )
@@ -0,0 +1,119 @@
1
+ from indico.queries import GraphQLRequest
2
+
3
+
4
+ class GetTeachDetails(GraphQLRequest):
5
+ GET_TEACH_DETAILS = """
6
+ query getCrowdlabelQuestionnaire($teach_task_id: Int!) {
7
+ questionnaire(id: $teach_task_id) {
8
+ id
9
+ question {
10
+ id
11
+ type
12
+ text
13
+ modelGroupId
14
+ status
15
+ labelset {
16
+ id
17
+ targetNames {
18
+ id
19
+ name
20
+ position
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ """
27
+
28
+ def __init__(self, *, teach_task_id: int):
29
+ super().__init__(
30
+ self.GET_TEACH_DETAILS,
31
+ variables={"teach_task_id": teach_task_id},
32
+ )
33
+
34
+ def process_response(self, response):
35
+ return super().process_response(response)
36
+
37
+
38
+ class GetExampleIds(GraphQLRequest):
39
+ GET_EXAMPLES = """
40
+ query getExamplesList(
41
+ $modelGroupId: Int!,
42
+ $filters: ExampleFilter,
43
+ $skip: Int,
44
+ $before: Int,
45
+ $after: Int,
46
+ $limit: Int,
47
+ $desc: Boolean,
48
+ $orderBy: ExampleOrder
49
+ ) {
50
+ modelGroup(modelGroupId: $modelGroupId) {
51
+ id
52
+ pagedExamples(
53
+ filters: $filters,
54
+ skip: $skip,
55
+ before: $before,
56
+ after: $after,
57
+ limit: $limit,
58
+ desc: $desc,
59
+ orderBy:
60
+ $orderBy
61
+ ) {
62
+ examples {
63
+ id
64
+ datarowId
65
+ updatedAt
66
+ status
67
+ datafile {
68
+ name
69
+ }
70
+ }
71
+ pageInfo {
72
+ startCursor
73
+ endCursor
74
+ hasNextPage
75
+ aggregateCount
76
+ }
77
+ }
78
+ }
79
+ }
80
+ """
81
+
82
+ def __init__(self, *, model_group_id: int, limit: int):
83
+ super().__init__(
84
+ self.GET_EXAMPLES,
85
+ variables={"modelGroupId": model_group_id, "limit": limit},
86
+ )
87
+
88
+ def process_response(self, response):
89
+ return super().process_response(response)
90
+
91
+
92
+ class LabelTeachTask(GraphQLRequest):
93
+ LABEL_TASK = """
94
+ mutation submitQuestionnaireExample
95
+ ($labelsetId: Int!,
96
+ $labels: [LabelInput]!,
97
+ $modelGroupId: Int) {
98
+ submitLabelsV2(
99
+ labels: $labels,
100
+ labelsetId: $labelsetId,
101
+ modelGroupId: $modelGroupId) {
102
+ success
103
+ __typename
104
+ }
105
+ }
106
+ """
107
+
108
+ def __init__(self, *, label_set_id: int, labels, model_group_id: int):
109
+ super().__init__(
110
+ self.LABEL_TASK,
111
+ variables={
112
+ "labelsetId": label_set_id,
113
+ "labels": labels,
114
+ "modelGroupId": model_group_id,
115
+ },
116
+ )
117
+
118
+ def process_response(self, response):
119
+ return super().process_response(response)