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.
- indico_toolkit/__init__.py +24 -0
- indico_toolkit/association/__init__.py +15 -0
- indico_toolkit/association/association.py +65 -0
- indico_toolkit/association/extracted_tokens.py +106 -0
- indico_toolkit/association/line_items.py +189 -0
- indico_toolkit/association/positioning.py +301 -0
- indico_toolkit/association/splitting.py +41 -0
- indico_toolkit/auto_populate/__init__.py +3 -0
- indico_toolkit/auto_populate/populator.py +362 -0
- indico_toolkit/auto_populate/types.py +66 -0
- indico_toolkit/auto_review/__init__.py +6 -0
- indico_toolkit/auto_review/auto_review_functions.py +123 -0
- indico_toolkit/auto_review/auto_reviewer.py +62 -0
- indico_toolkit/client.py +35 -0
- indico_toolkit/errors.py +32 -0
- indico_toolkit/etloutput/__init__.py +210 -0
- indico_toolkit/etloutput/cell.py +47 -0
- indico_toolkit/etloutput/errors.py +16 -0
- indico_toolkit/etloutput/etloutput.py +110 -0
- indico_toolkit/etloutput/range.py +30 -0
- indico_toolkit/etloutput/table.py +44 -0
- indico_toolkit/etloutput/token.py +25 -0
- indico_toolkit/indico_wrapper/__init__.py +15 -0
- indico_toolkit/indico_wrapper/dataset.py +114 -0
- indico_toolkit/indico_wrapper/doc_extraction.py +79 -0
- indico_toolkit/indico_wrapper/download.py +193 -0
- indico_toolkit/indico_wrapper/indico_wrapper.py +119 -0
- indico_toolkit/indico_wrapper/modelop.py +200 -0
- indico_toolkit/indico_wrapper/reviewer.py +111 -0
- indico_toolkit/indico_wrapper/workflow.py +231 -0
- indico_toolkit/metrics/__init__.py +11 -0
- indico_toolkit/metrics/compare_ground_truth.py +123 -0
- indico_toolkit/metrics/compare_models.py +180 -0
- indico_toolkit/metrics/metrics.py +373 -0
- indico_toolkit/metrics/plotting.py +102 -0
- indico_toolkit/ocr/__init__.py +9 -0
- indico_toolkit/ocr/customocr_object.py +42 -0
- indico_toolkit/ocr/ondoc_object.py +93 -0
- indico_toolkit/ocr/standard_object.py +54 -0
- indico_toolkit/pipelines/__init__.py +3 -0
- indico_toolkit/pipelines/file_processing.py +157 -0
- indico_toolkit/polling/__init__.py +8 -0
- indico_toolkit/polling/autoreview.py +204 -0
- indico_toolkit/polling/downstream.py +145 -0
- indico_toolkit/polling/queries.py +68 -0
- indico_toolkit/py.typed +0 -0
- indico_toolkit/results/__init__.py +104 -0
- indico_toolkit/results/document.py +78 -0
- indico_toolkit/results/errors.py +4 -0
- indico_toolkit/results/model.py +60 -0
- indico_toolkit/results/normalization.py +159 -0
- indico_toolkit/results/predictionlist.py +395 -0
- indico_toolkit/results/predictions/__init__.py +88 -0
- indico_toolkit/results/predictions/box.py +58 -0
- indico_toolkit/results/predictions/citation.py +47 -0
- indico_toolkit/results/predictions/classification.py +50 -0
- indico_toolkit/results/predictions/documentextraction.py +160 -0
- indico_toolkit/results/predictions/extraction.py +35 -0
- indico_toolkit/results/predictions/formextraction.py +121 -0
- indico_toolkit/results/predictions/group.py +29 -0
- indico_toolkit/results/predictions/prediction.py +41 -0
- indico_toolkit/results/predictions/span.py +43 -0
- indico_toolkit/results/predictions/summarization.py +103 -0
- indico_toolkit/results/predictions/unbundling.py +51 -0
- indico_toolkit/results/result.py +152 -0
- indico_toolkit/results/review.py +32 -0
- indico_toolkit/results/utilities.py +73 -0
- indico_toolkit/retry.py +94 -0
- indico_toolkit/snapshots/__init__.py +3 -0
- indico_toolkit/snapshots/snapshot.py +340 -0
- indico_toolkit/structure/__init__.py +3 -0
- indico_toolkit/structure/create_structure.py +232 -0
- indico_toolkit/structure/queries.py +119 -0
- indico_toolkit/structure/utils.py +16 -0
- indico_toolkit/types/__init__.py +12 -0
- indico_toolkit/types/classification.py +92 -0
- indico_toolkit/types/extractions.py +227 -0
- indico_toolkit/types/predictions.py +28 -0
- indico_toolkit/types/workflow_object.py +133 -0
- indico_toolkit-6.14.0.dist-info/LICENSE +21 -0
- indico_toolkit-6.14.0.dist-info/METADATA +144 -0
- indico_toolkit-6.14.0.dist-info/RECORD +83 -0
- indico_toolkit-6.14.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ModelTaskType(Enum):
|
|
5
|
+
"""
|
|
6
|
+
Issues with SDK 7/15/22-- need to hard code this here, new SDK release with fix is
|
|
7
|
+
imminent
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
CLASSIFICATION = 1
|
|
11
|
+
FORM_EXTRACTION = 2
|
|
12
|
+
OBJECT_DETECTION = 3
|
|
13
|
+
CLASSIFICATION_MULTIPLE = 4
|
|
14
|
+
REGRESSION = 5
|
|
15
|
+
ANNOTATION = 6
|
|
16
|
+
CLASSIFICATION_UNBUNDLING = 7
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .classification import Classification, ClassificationMGP
|
|
2
|
+
from .extractions import Extractions
|
|
3
|
+
from .predictions import Predictions
|
|
4
|
+
from .workflow_object import WorkflowResult
|
|
5
|
+
|
|
6
|
+
__all__ = (
|
|
7
|
+
"Classification",
|
|
8
|
+
"ClassificationMGP",
|
|
9
|
+
"Extractions",
|
|
10
|
+
"Predictions",
|
|
11
|
+
"WorkflowResult",
|
|
12
|
+
)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from operator import itemgetter
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from ..pipelines import FileProcessing
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
_PANDAS_INSTALLED = True
|
|
10
|
+
except ImportError as error:
|
|
11
|
+
_PANDAS_INSTALLED = False
|
|
12
|
+
_IMPORT_ERROR = error
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Classification:
|
|
16
|
+
"""
|
|
17
|
+
Functionality for classification predictions
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, prediction: dict):
|
|
21
|
+
self._pred = prediction
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def label(self) -> str:
|
|
25
|
+
return self._pred["label"]
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def labels(self) -> list:
|
|
29
|
+
return list(self.confidence_scores.keys())
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def confidence(self) -> float:
|
|
33
|
+
return self.confidence_scores[self.label]
|
|
34
|
+
|
|
35
|
+
def set_confidence_key_to_max_value(self):
|
|
36
|
+
"""
|
|
37
|
+
Overwite confidence dictionary to just max confidence float
|
|
38
|
+
"""
|
|
39
|
+
self._pred["confidence"] = self._pred["confidence"][self.label]
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def confidence_scores(self) -> Dict[str, float]:
|
|
43
|
+
return self._pred["confidence"]
|
|
44
|
+
|
|
45
|
+
def to_csv(
|
|
46
|
+
self, save_path, filename: str = "", append_if_exists: bool = True
|
|
47
|
+
) -> None:
|
|
48
|
+
if not _PANDAS_INSTALLED:
|
|
49
|
+
raise RuntimeError(
|
|
50
|
+
"saving predictions to CSV requires additional dependencies: "
|
|
51
|
+
"`pip install indico-toolkit[predictions]`"
|
|
52
|
+
) from _IMPORT_ERROR
|
|
53
|
+
|
|
54
|
+
results = {filename: self._pred}
|
|
55
|
+
df = pd.DataFrame(results).transpose()
|
|
56
|
+
df["filename"] = filename
|
|
57
|
+
if append_if_exists and FileProcessing.file_exists(save_path):
|
|
58
|
+
df.to_csv(save_path, mode="a", header=False, index=False)
|
|
59
|
+
else:
|
|
60
|
+
df.to_csv(save_path, index=False)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ClassificationMGP(Classification):
|
|
64
|
+
"""
|
|
65
|
+
Classification wrapper to account for
|
|
66
|
+
ModelGroupPredict returning a varied json output compared with Workflow
|
|
67
|
+
|
|
68
|
+
EXAMPLE:::
|
|
69
|
+
{'Type A': 0.99, 'Type B': 0.01}
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def label(self) -> str:
|
|
74
|
+
return max(self._pred.items(), key=itemgetter(1))[0]
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def labels(self) -> list:
|
|
78
|
+
return list(self._pred.keys())
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def confidence(self) -> float:
|
|
82
|
+
return self._pred[self.label]
|
|
83
|
+
|
|
84
|
+
def set_confidence_key_to_max_value(self):
|
|
85
|
+
"""
|
|
86
|
+
Overwite confidence dictionary to just max confidence float
|
|
87
|
+
"""
|
|
88
|
+
self._pred["confidence"] = self.confidence
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def confidence_scores(self) -> Dict[str, float]:
|
|
92
|
+
return self._pred
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
from collections import Counter, defaultdict
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from typing import Dict, Iterable, List, Set, Union
|
|
4
|
+
|
|
5
|
+
from ..errors import ToolkitInputError
|
|
6
|
+
from ..pipelines import FileProcessing
|
|
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
|
+
|
|
17
|
+
class Extractions:
|
|
18
|
+
"""
|
|
19
|
+
Functionality for common extraction prediction use cases
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, predictions: List[dict]):
|
|
23
|
+
self._preds = predictions
|
|
24
|
+
self.removed_predictions = []
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def to_dict_by_label(self) -> Dict[str, list]:
|
|
28
|
+
"""
|
|
29
|
+
Generate a dictionary where key is label string and value is list of all
|
|
30
|
+
predictions of that label
|
|
31
|
+
"""
|
|
32
|
+
prediction_label_map = defaultdict(list)
|
|
33
|
+
for pred in self._preds:
|
|
34
|
+
prediction_label_map[pred["label"]].append(pred)
|
|
35
|
+
return prediction_label_map
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def num_predictions(self) -> int:
|
|
39
|
+
return len(self._preds)
|
|
40
|
+
|
|
41
|
+
def remove_by_confidence(self, confidence: float = 0.95, labels: List[str] = None):
|
|
42
|
+
"""
|
|
43
|
+
Remove predictions that are less than given confidence
|
|
44
|
+
Args:
|
|
45
|
+
confidence (float, optional): confidence theshold. Defaults to 0.95.
|
|
46
|
+
labels (List[str], optional): Labels where this applies,
|
|
47
|
+
if None applies to all. Defaults to None.
|
|
48
|
+
"""
|
|
49
|
+
high_conf_preds = []
|
|
50
|
+
for pred in self._preds:
|
|
51
|
+
label = pred["label"]
|
|
52
|
+
if labels and label not in labels:
|
|
53
|
+
high_conf_preds.append(pred)
|
|
54
|
+
elif pred["confidence"][label] >= confidence:
|
|
55
|
+
high_conf_preds.append(pred)
|
|
56
|
+
else:
|
|
57
|
+
self.removed_predictions.append(pred)
|
|
58
|
+
self._preds = high_conf_preds
|
|
59
|
+
|
|
60
|
+
def remove_except_max_confidence(self, labels: List[str]):
|
|
61
|
+
"""
|
|
62
|
+
Removes all predictions except the highest confidence within each specified
|
|
63
|
+
class
|
|
64
|
+
"""
|
|
65
|
+
label_set = self.label_set
|
|
66
|
+
for label in labels:
|
|
67
|
+
if label not in label_set:
|
|
68
|
+
continue
|
|
69
|
+
max_pred = self._select_max_confidence(label)
|
|
70
|
+
self.remove_all_by_label([label])
|
|
71
|
+
self.removed_predictions.remove(max_pred)
|
|
72
|
+
self._preds.append(max_pred)
|
|
73
|
+
|
|
74
|
+
def set_confidence_key_to_max_value(self, inplace: bool = True):
|
|
75
|
+
"""
|
|
76
|
+
Overwite confidence dictionary to just max confidence float to make preds more
|
|
77
|
+
readable.
|
|
78
|
+
"""
|
|
79
|
+
if inplace:
|
|
80
|
+
self._set_confidence_key_to_max_value(self._preds)
|
|
81
|
+
else:
|
|
82
|
+
return self._set_confidence_key_to_max_value(deepcopy(self._preds))
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _set_confidence_key_to_max_value(preds):
|
|
86
|
+
for pred in preds:
|
|
87
|
+
pred["confidence"] = pred["confidence"][pred["label"]]
|
|
88
|
+
return preds
|
|
89
|
+
|
|
90
|
+
def remove_keys(self, keys_to_remove: List[str] = ["start", "end"]):
|
|
91
|
+
"""
|
|
92
|
+
Remove specified keys from prediction dictionaries
|
|
93
|
+
"""
|
|
94
|
+
for pred in self._preds:
|
|
95
|
+
for key in keys_to_remove:
|
|
96
|
+
pred.pop(key)
|
|
97
|
+
|
|
98
|
+
def remove_all_by_label(self, labels: Iterable[str]):
|
|
99
|
+
"""
|
|
100
|
+
Remove all predictions in list of labels
|
|
101
|
+
"""
|
|
102
|
+
preds = []
|
|
103
|
+
for p in self._preds:
|
|
104
|
+
if p["label"] in labels:
|
|
105
|
+
self.removed_predictions.append(p)
|
|
106
|
+
else:
|
|
107
|
+
preds.append(p)
|
|
108
|
+
self._preds = preds
|
|
109
|
+
|
|
110
|
+
def remove_human_added_predictions(self):
|
|
111
|
+
"""
|
|
112
|
+
Remove predictions that were not added by the model (i.e. added by scripted or
|
|
113
|
+
human review)
|
|
114
|
+
"""
|
|
115
|
+
self._preds = [
|
|
116
|
+
i for i in self._preds if not self.is_manually_added_prediction(i)
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
def to_list(self):
|
|
120
|
+
return self._preds
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def is_manually_added_prediction(prediction: dict) -> bool:
|
|
124
|
+
if isinstance(prediction["start"], int) and isinstance(prediction["end"], int):
|
|
125
|
+
if prediction["end"] > prediction["start"]:
|
|
126
|
+
return False
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def get_label_set(predictions: List[dict]) -> Set[str]:
|
|
131
|
+
"""
|
|
132
|
+
Get the included labels from a list of predictions
|
|
133
|
+
"""
|
|
134
|
+
return set([i["label"] for i in predictions])
|
|
135
|
+
|
|
136
|
+
def get_text_values(self, label: str) -> List[str]:
|
|
137
|
+
"""
|
|
138
|
+
Get all text values for a given label
|
|
139
|
+
"""
|
|
140
|
+
return [i["text"] for i in self._preds if i["label"] == label]
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def label_set(self):
|
|
144
|
+
return self.get_label_set(self._preds)
|
|
145
|
+
|
|
146
|
+
def _select_max_confidence(self, label: str) -> dict:
|
|
147
|
+
"""
|
|
148
|
+
Get the highest confidence prediction for a given field
|
|
149
|
+
"""
|
|
150
|
+
max_pred = None
|
|
151
|
+
confidence = 0
|
|
152
|
+
for pred in self[label]:
|
|
153
|
+
pred_confidence = pred["confidence"][label]
|
|
154
|
+
if pred_confidence >= confidence:
|
|
155
|
+
max_pred = pred
|
|
156
|
+
confidence = pred_confidence
|
|
157
|
+
return max_pred
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def label_count_dict(self) -> Dict[str, int]:
|
|
161
|
+
"""
|
|
162
|
+
Get count of occurrences of each label
|
|
163
|
+
"""
|
|
164
|
+
return dict(Counter(i["label"] for i in self._preds))
|
|
165
|
+
|
|
166
|
+
def exist_multiple_vals_for_label(self, label: str) -> bool:
|
|
167
|
+
"""
|
|
168
|
+
Check whether multiple unique text vals for field
|
|
169
|
+
"""
|
|
170
|
+
if len(set(self.get_text_values(label))) > 1:
|
|
171
|
+
return True
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
def get_most_common_text_value(self, label: str) -> Union[str, None]:
|
|
175
|
+
"""
|
|
176
|
+
Return the most common text value. If there is a tie- returns None.
|
|
177
|
+
"""
|
|
178
|
+
if label not in self.label_set:
|
|
179
|
+
raise ToolkitInputError(f"There are no predictions for: '{label}'")
|
|
180
|
+
text_vals = self.get_text_values(label)
|
|
181
|
+
if len(set(text_vals)) == 1:
|
|
182
|
+
return text_vals[0]
|
|
183
|
+
most_common = Counter(text_vals).most_common(2)
|
|
184
|
+
if most_common[0][1] > most_common[1][1]:
|
|
185
|
+
return most_common[0][0]
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
def __len__(self):
|
|
189
|
+
return len(self._preds)
|
|
190
|
+
|
|
191
|
+
def __repr__(self):
|
|
192
|
+
return f"{self.num_predictions} Extractions:\n\n{self._preds}"
|
|
193
|
+
|
|
194
|
+
def __getitem__(self, label: str) -> List[dict]:
|
|
195
|
+
return [i for i in self._preds if i["label"] == label]
|
|
196
|
+
|
|
197
|
+
def to_csv(
|
|
198
|
+
self,
|
|
199
|
+
save_path: str,
|
|
200
|
+
filename: str = "",
|
|
201
|
+
append_if_exists: bool = True,
|
|
202
|
+
include_start_end: bool = False,
|
|
203
|
+
) -> None:
|
|
204
|
+
"""
|
|
205
|
+
Write three column CSV ('confidence', 'label', 'text')
|
|
206
|
+
Args:
|
|
207
|
+
save_path (str): path to write CSV
|
|
208
|
+
include_start_end (bool): include columns for start/end indexes
|
|
209
|
+
append_if_exists (bool): if path exists, append to that CSV
|
|
210
|
+
filename (str, optional): the file where the preds were derived from.
|
|
211
|
+
Defaults to "".
|
|
212
|
+
"""
|
|
213
|
+
if not _PANDAS_INSTALLED:
|
|
214
|
+
raise RuntimeError(
|
|
215
|
+
"saving predictions to CSV requires additional dependencies: "
|
|
216
|
+
"`pip install indico-toolkit[predictions]`"
|
|
217
|
+
) from _IMPORT_ERROR
|
|
218
|
+
|
|
219
|
+
preds = self.set_confidence_key_to_max_value(inplace=False)
|
|
220
|
+
df = pd.DataFrame(preds)
|
|
221
|
+
if not include_start_end:
|
|
222
|
+
df.drop(["start", "end"], axis=1, inplace=True)
|
|
223
|
+
df["filename"] = filename
|
|
224
|
+
if append_if_exists and FileProcessing.file_exists(save_path):
|
|
225
|
+
df.to_csv(save_path, mode="a", header=False, index=False)
|
|
226
|
+
else:
|
|
227
|
+
df.to_csv(save_path, index=False)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from ..errors import ToolkitInputError
|
|
2
|
+
from .classification import Classification, ClassificationMGP
|
|
3
|
+
from .extractions import Extractions
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Predictions:
|
|
7
|
+
"""
|
|
8
|
+
Factory class for predictions
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def get_obj(predictions):
|
|
13
|
+
"""
|
|
14
|
+
Returns:
|
|
15
|
+
Extractions object or Classification object depending on predictions type
|
|
16
|
+
"""
|
|
17
|
+
if isinstance(predictions, list):
|
|
18
|
+
return Extractions(predictions)
|
|
19
|
+
elif isinstance(predictions, dict):
|
|
20
|
+
if "label" in predictions:
|
|
21
|
+
return Classification(predictions)
|
|
22
|
+
else:
|
|
23
|
+
return ClassificationMGP(predictions)
|
|
24
|
+
else:
|
|
25
|
+
raise ToolkitInputError(
|
|
26
|
+
f"Unable to process predictions with type {type(predictions)}. "
|
|
27
|
+
f"Predictions: {predictions}"
|
|
28
|
+
)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from ..errors import ToolkitInputError
|
|
4
|
+
from .predictions import Predictions
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WorkflowResult:
|
|
8
|
+
def __init__(self, result: dict, model_name: str = None):
|
|
9
|
+
"""
|
|
10
|
+
Common functionality for workflow result object
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
result (dict): raw workflow result object
|
|
14
|
+
model_name (str, optional): Extraction/Classification model name.
|
|
15
|
+
Defaults to None.
|
|
16
|
+
"""
|
|
17
|
+
self.result = result
|
|
18
|
+
self.model_name = model_name
|
|
19
|
+
|
|
20
|
+
def _check_is_valid_model_name(self) -> None:
|
|
21
|
+
if self.model_name not in self.available_model_names:
|
|
22
|
+
raise ToolkitInputError(
|
|
23
|
+
f"{self.model_name} is not an available model name. "
|
|
24
|
+
f"Options: {self.available_model_names}"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def __repr__(self):
|
|
28
|
+
return f"WorkflowResult object, Submission ID: {self.submission_id}"
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def get_predictions(self) -> Predictions:
|
|
32
|
+
"""
|
|
33
|
+
Return predictions without human review
|
|
34
|
+
"""
|
|
35
|
+
self._set_model_name()
|
|
36
|
+
preds = self.document_results[self.model_name]
|
|
37
|
+
if "pre_review" in preds:
|
|
38
|
+
preds = preds["pre_review"]
|
|
39
|
+
return Predictions.get_obj(preds)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def pre_review_predictions(self) -> Predictions:
|
|
43
|
+
"""
|
|
44
|
+
Return predictions before human review
|
|
45
|
+
"""
|
|
46
|
+
self._set_model_name()
|
|
47
|
+
preds = self.document_results[self.model_name]
|
|
48
|
+
if "pre_review" in preds:
|
|
49
|
+
preds = preds["pre_review"]
|
|
50
|
+
return Predictions.get_obj(preds)
|
|
51
|
+
else:
|
|
52
|
+
return Predictions.get_obj([])
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def post_reviews_predictions(self) -> Predictions:
|
|
56
|
+
"""
|
|
57
|
+
Return predictions after human review
|
|
58
|
+
"""
|
|
59
|
+
self._set_model_name()
|
|
60
|
+
preds = self.document_results[self.model_name]
|
|
61
|
+
if "post_reviews" in preds:
|
|
62
|
+
post_review_preds = []
|
|
63
|
+
reviews = preds["post_reviews"]
|
|
64
|
+
for review in reviews:
|
|
65
|
+
post_review_preds.append(Predictions.get_obj(review))
|
|
66
|
+
return post_review_preds
|
|
67
|
+
else:
|
|
68
|
+
return Predictions.get_obj([])
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def final_predictions(self) -> Predictions:
|
|
72
|
+
self._set_model_name()
|
|
73
|
+
preds = self.document_results[self.model_name]
|
|
74
|
+
if "final" in preds:
|
|
75
|
+
preds = preds["final"]
|
|
76
|
+
return Predictions.get_obj(preds)
|
|
77
|
+
else:
|
|
78
|
+
return Predictions.get_obj([])
|
|
79
|
+
|
|
80
|
+
def _set_model_name(self):
|
|
81
|
+
"""
|
|
82
|
+
Attempts to select the relevant model name if not already specified.
|
|
83
|
+
Raises error if multiple models are available and self.model_name specified.
|
|
84
|
+
"""
|
|
85
|
+
if self.model_name:
|
|
86
|
+
self._check_is_valid_model_name()
|
|
87
|
+
elif len(self.available_model_names) > 1:
|
|
88
|
+
raise ToolkitInputError(
|
|
89
|
+
"Multiple models available, you must set self.model_name to one of "
|
|
90
|
+
f"{self.available_model_names}"
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
self.model_name = self.available_model_names[0]
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def etl_url(self) -> str:
|
|
97
|
+
return self.result["etl_output"]
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def document_results(self) -> dict:
|
|
101
|
+
return self.result["results"]["document"]["results"]
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def available_model_names(self) -> List[str]:
|
|
105
|
+
return list(self.document_results.keys())
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def submission_id(self) -> str:
|
|
109
|
+
return self.result["submission_id"]
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def errors(self) -> list:
|
|
113
|
+
return self.result["errors"]
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def review_id(self) -> int:
|
|
117
|
+
return self.result["review_id"]
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def reviewer_id(self) -> int:
|
|
121
|
+
return self.result["reviewer_id"]
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def review_notes(self) -> int:
|
|
125
|
+
return self.result["review_notes"]
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def review_rejected(self) -> int:
|
|
129
|
+
return self.result["review_rejected"]
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def admin_review(self) -> bool:
|
|
133
|
+
return self.result["admin_review"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 IndicoDataSolutions
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: indico-toolkit
|
|
3
|
+
Version: 6.14.0
|
|
4
|
+
Summary: Classes, functions, and abstractions for Indico IPA
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Indico Solutions Engineering
|
|
7
|
+
Author-email: solutionsengineering@indicodata.ai
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Provides-Extra: downloads
|
|
17
|
+
Provides-Extra: examples
|
|
18
|
+
Provides-Extra: metrics
|
|
19
|
+
Provides-Extra: predictions
|
|
20
|
+
Provides-Extra: snapshots
|
|
21
|
+
Requires-Dist: indico-client (>=6.14.0,<7.0.0)
|
|
22
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0) ; extra == "all"
|
|
23
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0) ; extra == "downloads"
|
|
24
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0) ; extra == "examples"
|
|
25
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0) ; extra == "metrics"
|
|
26
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0) ; extra == "predictions"
|
|
27
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0) ; extra == "snapshots"
|
|
28
|
+
Requires-Dist: plotly (>=5.2.1,<6.0.0) ; extra == "all"
|
|
29
|
+
Requires-Dist: plotly (>=5.2.1,<6.0.0) ; extra == "metrics"
|
|
30
|
+
Requires-Dist: tqdm (>=4.50.0,<5.0.0) ; extra == "all"
|
|
31
|
+
Requires-Dist: tqdm (>=4.50.0,<5.0.0) ; extra == "downloads"
|
|
32
|
+
Project-URL: source, https://github.com/IndicoDataSolutions/Indico-Solutions-Toolkit
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Indico Toolkit
|
|
36
|
+
|
|
37
|
+
**This repository contains software that is not officially supported by Indico. It may
|
|
38
|
+
be outdated or contain bugs. The operations it performs are potentially destructive.
|
|
39
|
+
Use at your own risk.**
|
|
40
|
+
|
|
41
|
+
Classes, functions, and abstractions for building workflows using the Indico IPA
|
|
42
|
+
(Intelligent Process Automation) platform.
|
|
43
|
+
|
|
44
|
+
- [Polling Classes](https://github.com/IndicoDataSolutions/indico-toolkit-python/tree/main/indico_toolkit/polling/__init__.py)
|
|
45
|
+
that implement best-practices polling behavior for Auto Review and Downstream
|
|
46
|
+
processes. Easily plug in business logic without the boilerplate.
|
|
47
|
+
- [Result File](https://github.com/IndicoDataSolutions/indico-toolkit-python/blob/main/indico_toolkit/results/__init__.py)
|
|
48
|
+
and [Etl Output](https://github.com/IndicoDataSolutions/indico-toolkit-python/blob/main/indico_toolkit/etloutput/__init__.py)
|
|
49
|
+
Data Classes that parse standard IPA JSON output into idiomatic, type-safe Python dataclasses.
|
|
50
|
+
- [Metrics Classes](https://github.com/IndicoDataSolutions/indico-toolkit-python/blob/main/indico_toolkit/metrics/__init__.py)
|
|
51
|
+
to compare model performance, evaluate ground truth, and plot statistics.
|
|
52
|
+
- [Snapshot Classes](https://github.com/IndicoDataSolutions/indico-toolkit-python/blob/main/indico_toolkit/snapshots/snapshot.py)
|
|
53
|
+
to concatenate, merge, filter, and manipulate snapshot CSVs.
|
|
54
|
+
|
|
55
|
+
...and more in the [Examples](https://github.com/IndicoDataSolutions/indico-toolkit-python/tree/main/examples) folder.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
**Indico Toolkit does not use semantic versioning.**
|
|
61
|
+
|
|
62
|
+
Indico Toolkit versions match the minimum IPA version required to use its functionality.
|
|
63
|
+
E.g. `indico-toolkit==6.14.0` makes use of functionality introduced in IPA 6.14, and
|
|
64
|
+
some functionality requires IPA 6.14 or later to use.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install indico-toolkit
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Some functionality requires optional dependencies that can be installed with extras.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install 'indico-toolkit[all]'
|
|
74
|
+
pip install 'indico-toolkit[downloads]'
|
|
75
|
+
pip install 'indico-toolkit[examples]'
|
|
76
|
+
pip install 'indico-toolkit[metrics]'
|
|
77
|
+
pip install 'indico-toolkit[predictions]'
|
|
78
|
+
pip install 'indico-toolkit[snapshots]'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Contributing
|
|
83
|
+
|
|
84
|
+
Indico Toolkit uses Poetry 2.X for package and dependency management.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
### Setup
|
|
88
|
+
|
|
89
|
+
Clone the source repository with Git.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone git@github.com:IndicoDataSolutions/indico-toolkit-python.git
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Install dependencies with Poetry.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
poetry install
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Formatting, linting, type checking, and tests are defined as
|
|
102
|
+
[Poe](https://poethepoet.natn.io/) tasks in `pyproject.toml`.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
poetry run poe {format,check,test,all}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Code changes or additions should pass `poetry run poe all` before opening a PR.
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
### Tests
|
|
112
|
+
|
|
113
|
+
Indico Toolkit has three test suites: required unit tests, extra unit tests, and
|
|
114
|
+
integration tests.
|
|
115
|
+
|
|
116
|
+
By default, only required unit tests are executed. Extra unit tests and integration
|
|
117
|
+
tests are skipped.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
poetry run poe {test,all}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Extra unit tests are skipped when their dependencies are not installed. To execute extra
|
|
124
|
+
unit tests, install one or more extras and run the tests.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
poetry install --all-extras
|
|
128
|
+
poetry run poe {test,all}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Integration tests make API calls to an IPA environment and require a host and API token
|
|
132
|
+
to execute. These tests create datasets, setup workflows, and train models. **Expect
|
|
133
|
+
them to take tens of minutes to run.**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
poetry run poe test-integration \
|
|
137
|
+
--host try.indico.io \
|
|
138
|
+
--token indico_api_token.txt
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Make liberal use of pytest's `--last-failed` and `--failed-first`
|
|
142
|
+
[flags](https://docs.pytest.org/en/stable/how-to/cache.html) to speed up integration
|
|
143
|
+
test execution when writing code.
|
|
144
|
+
|