indico_toolkit 6.0.1__tar.gz → 6.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/CHANGELOG.md +7 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/PKG-INFO +2 -9
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/README.md +0 -6
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/__init__.py +1 -1
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/__init__.py +3 -3
- indico_toolkit-6.1.0/indico_toolkit/association/splitting.py +41 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_populate/populator.py +43 -38
- indico_toolkit-6.1.0/indico_toolkit/indico_wrapper/modelop.py +192 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/workflow.py +25 -5
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/workflow_object.py +2 -4
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/pyproject.toml +2 -2
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/requirements.txt +1 -2
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/setup.py +1 -1
- indico_toolkit-6.1.0/tests/association/test_splitting.py +26 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/auto_populate/test_populator.py +0 -1
- indico_toolkit-6.1.0/tests/indico_wrapper/test_modelops.py +67 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/pipelines/test_file_processing.py +1 -1
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_worflow_result.py +14 -9
- indico_toolkit-6.0.1/indico_toolkit/association/split_merged_values.py +0 -50
- indico_toolkit-6.0.1/indico_toolkit/highlighter/__init__.py +0 -1
- indico_toolkit-6.0.1/indico_toolkit/highlighter/highlighter.py +0 -306
- indico_toolkit-6.0.1/indico_toolkit/pipelines/pdf_manipulation.py +0 -67
- indico_toolkit-6.0.1/indico_toolkit/staggered_loop/__init__.py +0 -2
- indico_toolkit-6.0.1/indico_toolkit/staggered_loop/metrics.py +0 -1433
- indico_toolkit-6.0.1/indico_toolkit/staggered_loop/queries.py +0 -42
- indico_toolkit-6.0.1/indico_toolkit/staggered_loop/staggered_loop.py +0 -724
- indico_toolkit-6.0.1/indico_toolkit/staggered_loop/staggered_loop_recruiter.py +0 -180
- indico_toolkit-6.0.1/tests/association/test_split_merged_values.py +0 -35
- indico_toolkit-6.0.1/tests/highlighter/data/fcc_doc.pdf +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/fcc_ocr_obj.p +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/fcc_predictions.p +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/highlighted_fcc.pdf +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/highlighted_invoice.pdf +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/invoice_sample.pdf +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/ocr_obj.p +0 -0
- indico_toolkit-6.0.1/tests/highlighter/data/predictions.p +0 -0
- indico_toolkit-6.0.1/tests/highlighter/test_highlighter.py +0 -195
- indico_toolkit-6.0.1/tests/integration/test_processing_pdfs.py +0 -22
- indico_toolkit-6.0.1/tests/pipelines/test_pdf_manipulation.py +0 -25
- indico_toolkit-6.0.1/tests/staggered_loop/__init__.py +0 -0
- indico_toolkit-6.0.1/tests/staggered_loop/test_staggered_loop.py +0 -162
- indico_toolkit-6.0.1/tests/staggered_loop/test_staggered_loop_recruiter.py +0 -24
- indico_toolkit-6.0.1/tests/types/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/.github/workflows/build.yml +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/.gitignore +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/LICENSE +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/auto_review_predictions.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/copy_teach_task.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/create_auto_classification_workflow.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/create_full_structure.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/dataset_tasks.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/ground_truth_metrics.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/manipulate_pdfs.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/merge_snapshots.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/model_metrics.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/pdf_highlighter.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/row_association.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/submitting_to_doc_extraction.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/submitting_to_workflow.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/association.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/extracted_tokens.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/line_items.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/positioning.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_populate/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_populate/types.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_review/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_review/auto_review_functions.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_review/auto_reviewer.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/client.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/errors.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/dataset.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/doc_extraction.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/download.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/indico_wrapper.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/reviewer.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/compare_ground_truth.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/compare_models.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/metrics.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/plotting.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/customocr_object.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/ondoc_object.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/standard_object.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/pipelines/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/pipelines/file_processing.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/retry.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/snapshots/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/snapshots/snapshot.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/create_structure.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/queries.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/utils.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/classification.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/extractions.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/predictions.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/conftest.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/three_row_invoice/preds.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/three_row_invoice/tokens.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/token_page/tokens.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/two_row_bank_statement/preds.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/two_row_bank_statement/tokens.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/test_extracted_tokens.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/test_line_items.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/test_positioning.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/auto_review/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/auto_review/test_auto_review.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/conftest.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/auto_class/class_a/sample_two_pages.pdf +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/auto_class/class_b/inv_sample2.pdf +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/auto_review/preds.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc.pdf +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc_classification.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc_result.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc_snapshot.csv +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/staggered_loop_recruiter_example.json +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/snapshots/populator_snapshot.csv +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/snapshots/snapshot.csv +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/snapshots/updated_snapshot.csv +0 -0
- {indico_toolkit-6.0.1/tests/highlighter → indico_toolkit-6.1.0/tests/indico_wrapper}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_dataset.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_doc_extraction.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_download.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_indico_wrapper.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_reviewer.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_workflow.py +0 -0
- {indico_toolkit-6.0.1/tests/indico_wrapper → indico_toolkit-6.1.0/tests/integration}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/integration/test_snapshot_upload.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/integration/test_workflow_rows.py +0 -0
- {indico_toolkit-6.0.1/tests/integration → indico_toolkit-6.1.0/tests/metrics}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_compare_ground_truth.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_compare_models.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_metrics.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_plotting.py +0 -0
- {indico_toolkit-6.0.1/tests/metrics → indico_toolkit-6.1.0/tests/ocr}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/ocr/test_customocr_object.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/ocr/test_ondoc_object.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/ocr/test_standard_object.py +0 -0
- {indico_toolkit-6.0.1/tests/ocr → indico_toolkit-6.1.0/tests/pipelines}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/pytest.ini +0 -0
- {indico_toolkit-6.0.1/tests/pipelines → indico_toolkit-6.1.0/tests/snapshots}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/snapshots/conftest.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/snapshots/test_snapshot.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/structure/test_create_structure.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/test_create_client.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/test_retry.py +0 -0
- {indico_toolkit-6.0.1/tests/snapshots → indico_toolkit-6.1.0/tests/types}/__init__.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/conftest.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_classifications.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_extractions.py +0 -0
- {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_predictions.py +0 -0
|
@@ -140,3 +140,10 @@ This is the first major version release tested to work on Indico 6.X.
|
|
|
140
140
|
### Added
|
|
141
141
|
|
|
142
142
|
* Small but important fix to add original filename to the workflow result object
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
## 6.1.0 5/6/24
|
|
146
|
+
|
|
147
|
+
### Removed
|
|
148
|
+
|
|
149
|
+
* Removed staggered loop support and removed highlighting support.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: indico_toolkit
|
|
3
|
-
Version: 6.0
|
|
3
|
+
Version: 6.1.0
|
|
4
4
|
Summary: A package to support Indico IPA development
|
|
5
5
|
Home-page: https://github.com/IndicoDataSolutions/Indico-Solutions-Toolkit
|
|
6
6
|
Author: indico
|
|
@@ -12,9 +12,8 @@ Requires-Dist: plotly==5.2.1
|
|
|
12
12
|
Requires-Dist: tqdm==4.50.0
|
|
13
13
|
Requires-Dist: faker==13.3.3
|
|
14
14
|
Requires-Dist: python-dateutil==2.8.1
|
|
15
|
-
Requires-Dist: PyMuPDF==1.19.6 ; extra == "full"
|
|
16
15
|
Requires-Dist: spacy>=3.1.4,<4 ; extra == "full"
|
|
17
|
-
Requires-Dist: pytest>=
|
|
16
|
+
Requires-Dist: pytest>=6.2.5 ; extra == "test"
|
|
18
17
|
Requires-Dist: requests-mock>=1.7.0-7 ; extra == "test"
|
|
19
18
|
Requires-Dist: pytest-dependency==0.5.1 ; extra == "test"
|
|
20
19
|
Provides-Extra: full
|
|
@@ -34,8 +33,6 @@ The indico-toolkit provides classes and functions to help achieve the following:
|
|
|
34
33
|
* Tools to assist with creating and copying workflow structures.
|
|
35
34
|
* Get metrics for all model IDs in a model group to see how well fields are performing after more labeling.
|
|
36
35
|
* Compare two models via bar plot and data tables.
|
|
37
|
-
* Highlighting extraction predictions on source PDFs.
|
|
38
|
-
* A simple Staggered loop implementation to identify high error documents and inject reviewed results into dev tasks.
|
|
39
36
|
* Train a document classification model without labeling.
|
|
40
37
|
* An AutoReview class to assist with automated acceptance/rejection of model predictions.
|
|
41
38
|
* Common manipulation of prediction/workflow results.
|
|
@@ -50,7 +47,6 @@ pip install indico_toolkit
|
|
|
50
47
|
|
|
51
48
|
* Note: if you are on Indico 6.X, install an indico_toolkit 6.X version. If you're on 5.X install a 2.X version.
|
|
52
49
|
* Note: If you are on a version of the Indico IPA platform pre-5.1, then install indico-toolkit==1.2.3.
|
|
53
|
-
* If you want to use PdfHighlighter, install with `pip install 'indico_toolkit[full]'` as PyMuPDF is an optional dependency.
|
|
54
50
|
|
|
55
51
|
### Example Useage
|
|
56
52
|
|
|
@@ -63,9 +59,6 @@ You can also set WORKFLOW_ID (workflow w/ single extraction model), MODEL_NAME (
|
|
|
63
59
|
and DATASET_ID (uploaded dataset). If you don't set these 3 env variables, test configuration will
|
|
64
60
|
upload a dataset and create a workflow.
|
|
65
61
|
|
|
66
|
-
Note: spacy isn't a requirement to install the package, but is a requirement to run the full test suite
|
|
67
|
-
as it is part of "staggered loop".
|
|
68
|
-
|
|
69
62
|
```
|
|
70
63
|
pytest
|
|
71
64
|
```
|
|
@@ -12,8 +12,6 @@ The indico-toolkit provides classes and functions to help achieve the following:
|
|
|
12
12
|
* Tools to assist with creating and copying workflow structures.
|
|
13
13
|
* Get metrics for all model IDs in a model group to see how well fields are performing after more labeling.
|
|
14
14
|
* Compare two models via bar plot and data tables.
|
|
15
|
-
* Highlighting extraction predictions on source PDFs.
|
|
16
|
-
* A simple Staggered loop implementation to identify high error documents and inject reviewed results into dev tasks.
|
|
17
15
|
* Train a document classification model without labeling.
|
|
18
16
|
* An AutoReview class to assist with automated acceptance/rejection of model predictions.
|
|
19
17
|
* Common manipulation of prediction/workflow results.
|
|
@@ -28,7 +26,6 @@ pip install indico_toolkit
|
|
|
28
26
|
|
|
29
27
|
* Note: if you are on Indico 6.X, install an indico_toolkit 6.X version. If you're on 5.X install a 2.X version.
|
|
30
28
|
* Note: If you are on a version of the Indico IPA platform pre-5.1, then install indico-toolkit==1.2.3.
|
|
31
|
-
* If you want to use PdfHighlighter, install with `pip install 'indico_toolkit[full]'` as PyMuPDF is an optional dependency.
|
|
32
29
|
|
|
33
30
|
### Example Useage
|
|
34
31
|
|
|
@@ -41,9 +38,6 @@ You can also set WORKFLOW_ID (workflow w/ single extraction model), MODEL_NAME (
|
|
|
41
38
|
and DATASET_ID (uploaded dataset). If you don't set these 3 env variables, test configuration will
|
|
42
39
|
upload a dataset and create a workflow.
|
|
43
40
|
|
|
44
|
-
Note: spacy isn't a requirement to install the package, but is a requirement to run the full test suite
|
|
45
|
-
as it is part of "staggered loop".
|
|
46
|
-
|
|
47
41
|
```
|
|
48
42
|
pytest
|
|
49
43
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from .association import Association, sequences_exact, sequences_overlap
|
|
2
2
|
from .extracted_tokens import ExtractedTokens
|
|
3
|
-
from .
|
|
3
|
+
from .line_items import LineItems
|
|
4
4
|
from .positioning import Positioning
|
|
5
|
-
from .
|
|
5
|
+
from .splitting import split_prediction
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from itertools import zip_longest
|
|
4
|
+
from typing import Any, Dict, Iterator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def split_prediction(
|
|
8
|
+
prediction: Dict[str, Any], separator: str = r"\s*\n\s*"
|
|
9
|
+
) -> Iterator[Dict[str, Any]]:
|
|
10
|
+
"""
|
|
11
|
+
Split a single `prediction` into mutiple predictions along a `separator` regex
|
|
12
|
+
and yield new predictions having updated text and spans. By default, split on
|
|
13
|
+
newlines (including adjacent whitespace).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# re.split (with group capture) returns a sequence of text values and delimiters:
|
|
17
|
+
# ["some", "\n", "separated", "\n", "text"]
|
|
18
|
+
# The text values are the even elements:
|
|
19
|
+
# ["some", "separated", "text"]
|
|
20
|
+
# The delimeters are the odd elements:
|
|
21
|
+
# ["\n", "\n"]
|
|
22
|
+
# zip_longest pairs them together without losing the last text value:
|
|
23
|
+
# [("some", "\n"), ("separated", "\n"), ("text", None)]
|
|
24
|
+
texts_and_delimiters = re.split(f"({separator})", prediction["text"])
|
|
25
|
+
texts = texts_and_delimiters[::2]
|
|
26
|
+
delimiters = texts_and_delimiters[1::2]
|
|
27
|
+
text_and_delimiter_pairs = zip_longest(texts, delimiters)
|
|
28
|
+
start = prediction["start"]
|
|
29
|
+
|
|
30
|
+
for text, delimiter in text_and_delimiter_pairs:
|
|
31
|
+
end = start + len(text)
|
|
32
|
+
|
|
33
|
+
yield {
|
|
34
|
+
**deepcopy(prediction),
|
|
35
|
+
"text": text,
|
|
36
|
+
"start": start,
|
|
37
|
+
"end": end,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if delimiter is not None:
|
|
41
|
+
start = end + len(delimiter)
|
|
@@ -2,7 +2,9 @@ import time
|
|
|
2
2
|
import dataclasses
|
|
3
3
|
import pandas as pd
|
|
4
4
|
from json import loads
|
|
5
|
-
from
|
|
5
|
+
from os import PathLike
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Dict, Tuple, Union
|
|
6
8
|
from indico import IndicoClient
|
|
7
9
|
from indico.types import Workflow
|
|
8
10
|
from indico.queries import (
|
|
@@ -13,7 +15,6 @@ from indico.queries import (
|
|
|
13
15
|
GetModelGroup,
|
|
14
16
|
)
|
|
15
17
|
from indico_toolkit.errors import ToolkitPopulationError
|
|
16
|
-
from indico_toolkit.pipelines import FileProcessing
|
|
17
18
|
from indico_toolkit.structure.create_structure import Structure
|
|
18
19
|
|
|
19
20
|
from .types import *
|
|
@@ -30,28 +31,31 @@ class AutoPopulator:
|
|
|
30
31
|
self.client = client
|
|
31
32
|
self.structure = Structure(client)
|
|
32
33
|
self._exceptions = []
|
|
33
|
-
self._fp = FileProcessing()
|
|
34
34
|
|
|
35
35
|
def create_auto_classification_workflow(
|
|
36
36
|
self,
|
|
37
|
-
directory_path: str,
|
|
37
|
+
directory_path: Union[str, PathLike[str]],
|
|
38
38
|
dataset_name: str,
|
|
39
39
|
workflow_name: str,
|
|
40
40
|
teach_task_name: str,
|
|
41
|
-
accepted_types: Tuple[str] = (
|
|
42
|
-
"pdf",
|
|
43
|
-
"tiff",
|
|
41
|
+
accepted_types: Tuple[str, ...] = (
|
|
44
42
|
"csv",
|
|
45
43
|
"doc",
|
|
46
44
|
"docx",
|
|
47
|
-
"
|
|
45
|
+
"eml",
|
|
48
46
|
"jpeg",
|
|
49
47
|
"jpg",
|
|
48
|
+
"msg",
|
|
49
|
+
"pdf",
|
|
50
|
+
"png",
|
|
50
51
|
"pptx",
|
|
52
|
+
"rtf",
|
|
53
|
+
"svg",
|
|
54
|
+
"tif",
|
|
55
|
+
"tiff",
|
|
51
56
|
"txt",
|
|
52
57
|
"xls",
|
|
53
58
|
"xlsx",
|
|
54
|
-
"rtf"
|
|
55
59
|
),
|
|
56
60
|
) -> Workflow:
|
|
57
61
|
"""
|
|
@@ -73,54 +77,55 @@ class AutoPopulator:
|
|
|
73
77
|
Returns:
|
|
74
78
|
Workflow: a Workflow object representation of the newly created workflow
|
|
75
79
|
"""
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
labelset_name = f"{teach_task_name}_labelset"
|
|
81
|
-
file_to_targets = dict(
|
|
82
|
-
zip(
|
|
83
|
-
[self._fp.file_name_from_path(f) for f in self._fp.file_paths],
|
|
84
|
-
[[{"label": parent_directory}] for parent_directory in self._fp.parent_directory_of_filepaths]
|
|
80
|
+
|
|
81
|
+
def valid_file(file: Path) -> bool:
|
|
82
|
+
return (
|
|
83
|
+
file.is_file() and file.suffix.strip(".").casefold() in accepted_types
|
|
85
84
|
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
|
|
86
|
+
folder = Path(directory_path)
|
|
87
|
+
files = list(filter(valid_file, folder.glob("*/*")))
|
|
88
|
+
classes = list(set(file.parent.name for file in files))
|
|
89
|
+
labeled_files = {file.name: [{"label": file.parent.name}] for file in files}
|
|
90
|
+
|
|
91
|
+
if len(classes) < 2:
|
|
92
|
+
raise ToolkitPopulationError(
|
|
93
|
+
f"You must have documents in at least 2 directories, you only have {len(classes)}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Upload files to a new dataset.
|
|
94
97
|
dataset = self.structure.create_dataset(
|
|
95
98
|
dataset_name=dataset_name,
|
|
96
|
-
files_to_upload=
|
|
99
|
+
files_to_upload=files,
|
|
97
100
|
read_api=True,
|
|
98
101
|
single_column=False,
|
|
99
|
-
|
|
102
|
+
auto_rotate=False,
|
|
103
|
+
upscale_images=True,
|
|
104
|
+
languages=["ENG"],
|
|
100
105
|
)
|
|
101
|
-
|
|
106
|
+
|
|
107
|
+
# Create a new workflow with classification model.
|
|
102
108
|
workflow = self.structure.create_workflow(workflow_name, dataset.id)
|
|
103
|
-
# Add classifier teach task to workflow (should know labels from parent directory)
|
|
104
109
|
workflow = self.structure.add_teach_task(
|
|
105
110
|
task_name=teach_task_name,
|
|
106
|
-
labelset_name=
|
|
111
|
+
labelset_name=f"{teach_task_name}_labelset",
|
|
107
112
|
target_names=classes,
|
|
108
113
|
dataset_id=dataset.id,
|
|
109
114
|
workflow_id=workflow.id,
|
|
110
115
|
model_type="classification",
|
|
111
116
|
)
|
|
112
117
|
teach_task_id = workflow.components[-1].model_group.questionnaire_id
|
|
113
|
-
labelset_id, model_group_id,
|
|
118
|
+
labelset_id, model_group_id, label_map = self._get_teach_task_details(
|
|
114
119
|
teach_task_id
|
|
115
120
|
)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
f"You must have documents in at least 2 directories, you only have {len(classes)}"
|
|
119
|
-
)
|
|
120
|
-
labels = self.get_labels_by_filename(model_group_id, file_to_targets, target_name_map)
|
|
121
|
+
|
|
122
|
+
labels = self.get_labels_by_filename(model_group_id, labeled_files, label_map)
|
|
121
123
|
self.structure.label_teach_task(
|
|
122
|
-
label_set_id=labelset_id,
|
|
124
|
+
label_set_id=labelset_id,
|
|
125
|
+
labels=list(map(dataclasses.asdict, labels)),
|
|
126
|
+
model_group_id=model_group_id,
|
|
123
127
|
)
|
|
128
|
+
|
|
124
129
|
return workflow
|
|
125
130
|
|
|
126
131
|
def copy_teach_task(
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
from indico import IndicoClient
|
|
2
|
+
from indico.queries import GraphQLRequest
|
|
3
|
+
import json
|
|
4
|
+
import string
|
|
5
|
+
from typing import Iterator
|
|
6
|
+
|
|
7
|
+
# valid model option parameters
|
|
8
|
+
TEXT_EXTRACTION_PARAMS = {
|
|
9
|
+
"max_empty_chunk_ratio": lambda value: 0 <= value <= 1.0e5,
|
|
10
|
+
"auto_negative_scaling": [True, False],
|
|
11
|
+
"optimize_for": [
|
|
12
|
+
"predict_speed",
|
|
13
|
+
"accuracy",
|
|
14
|
+
"speed",
|
|
15
|
+
"accuracy_fp16",
|
|
16
|
+
"predict_speed_fp16",
|
|
17
|
+
],
|
|
18
|
+
"subtoken_predictions": [True, False],
|
|
19
|
+
"base_model": [
|
|
20
|
+
"roberta",
|
|
21
|
+
"small",
|
|
22
|
+
"multilingual",
|
|
23
|
+
"fast",
|
|
24
|
+
"textcnn",
|
|
25
|
+
"fasttextcnn",
|
|
26
|
+
],
|
|
27
|
+
"class_weight": ["linear", "sqrt", "log", None],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
TEXT_CLASSIFICATION_PARAMS = {
|
|
31
|
+
"model_type": ["tfidf_lr", "tfidf_gbt", "standard", "finetune"]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ModelOp:
|
|
36
|
+
"""
|
|
37
|
+
Class to assit in Model Group Operation calls.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, client: IndicoClient):
|
|
41
|
+
self.client = client
|
|
42
|
+
|
|
43
|
+
def get_model_options(
|
|
44
|
+
self, model_group_id: int, model_id: int | None = None
|
|
45
|
+
) -> dict[str, object]:
|
|
46
|
+
"""
|
|
47
|
+
Return model options for `model_id` of `model_group_id`
|
|
48
|
+
or the most recent model if `model_id` is not specified.
|
|
49
|
+
Args:
|
|
50
|
+
model_group_id (int): id of model group
|
|
51
|
+
model_id (int, optional): argument to return a specific model within a model group
|
|
52
|
+
"""
|
|
53
|
+
all_model_options = self.get_all_model_options(model_group_id)
|
|
54
|
+
|
|
55
|
+
if model_id is None:
|
|
56
|
+
return next(all_model_options)
|
|
57
|
+
|
|
58
|
+
for model_options in all_model_options:
|
|
59
|
+
if model_options["id"] == model_id:
|
|
60
|
+
return model_options
|
|
61
|
+
else:
|
|
62
|
+
raise RuntimeError(
|
|
63
|
+
f"Model group {model_group_id} does not have a model with ID {model_id}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def get_all_model_options(self, model_group_id: int) -> Iterator[dict[str, object]]:
|
|
67
|
+
"""
|
|
68
|
+
Return model options for all models of `model_group_id`.
|
|
69
|
+
Args:
|
|
70
|
+
model_group_id (int): id of model group
|
|
71
|
+
Returns:
|
|
72
|
+
dict: dictionary of all model options, including:
|
|
73
|
+
"id", "domain, ""high_quality", "interlabeler_resolution",
|
|
74
|
+
"sampling_strategy", "seed", "test_split", "weight_by_class_frequency",
|
|
75
|
+
"word_predictor_strength", and "model_training_options"
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
response = self.client.call(
|
|
79
|
+
GraphQLRequest(
|
|
80
|
+
"""
|
|
81
|
+
query getMoonbowModelGroupModels($modelGroupId: Int!) {
|
|
82
|
+
modelGroup(modelGroupId: $modelGroupId) {
|
|
83
|
+
id
|
|
84
|
+
models {
|
|
85
|
+
id
|
|
86
|
+
modelOptions
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
""",
|
|
91
|
+
{"modelGroupId": model_group_id},
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
for model in response["modelGroup"]["models"]:
|
|
96
|
+
model_options = json.loads(model["modelOptions"])
|
|
97
|
+
model_options["id"] = model["id"]
|
|
98
|
+
yield model_options
|
|
99
|
+
|
|
100
|
+
def retrain_model(self, model_group_id: int) -> dict:
|
|
101
|
+
"""
|
|
102
|
+
Forces model retrain.
|
|
103
|
+
Args:
|
|
104
|
+
model_group_id (int): id of model group needing retraining
|
|
105
|
+
Returns:
|
|
106
|
+
dict: returns model group id and status of retraining model
|
|
107
|
+
"""
|
|
108
|
+
return self.client.call(
|
|
109
|
+
GraphQLRequest(
|
|
110
|
+
"""
|
|
111
|
+
mutation retrainMoonbowModelGroup($modelGroupId: Int!) {
|
|
112
|
+
retrainModelGroup(modelGroupId: $modelGroupId, forceRetrain: true) {
|
|
113
|
+
id
|
|
114
|
+
status
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
""",
|
|
118
|
+
{"modelGroupId": model_group_id},
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def update_model_settings(
|
|
123
|
+
self, model_group_id: int, model_type: str, **model_training_options
|
|
124
|
+
) -> dict[str, object]:
|
|
125
|
+
"""
|
|
126
|
+
Update group model settings based on specified model training options.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
model_group_id (int): id of model group
|
|
130
|
+
model_type (str): type of model ("text_extraction", "text_classification")
|
|
131
|
+
**kwargs: Advanced Model Training Options
|
|
132
|
+
|
|
133
|
+
Default values of Advanced Model Training Options and the avaiable parameters:
|
|
134
|
+
For Text Extraction Model:
|
|
135
|
+
max_empty_chunk_ratio : 1.0 (min of 0, no max value: a large number effectivly turns this option off)
|
|
136
|
+
auto_negative_scaling : True
|
|
137
|
+
optimize_for : "predict_speed" ( "predict_speed", "accuracy", "speed", "accuracy_fp16" and "predict_speed_fp16")
|
|
138
|
+
subtoken_predictions : True
|
|
139
|
+
base_model : "roberta" ("roberta", "small" (distilled version of RoBERTa), "multilingual", "fast", "textcnn", "fasttextcnn")
|
|
140
|
+
class_weight : "sqrt" ("linear", "sqrt", "log", None)
|
|
141
|
+
|
|
142
|
+
For Text Classification Model:
|
|
143
|
+
model_type : "standard" (“tfidf_lr”, “tfidf_gbt”, “standard”, “finetune”)
|
|
144
|
+
Returns:
|
|
145
|
+
dict: Dictionary of advanced model training options
|
|
146
|
+
"""
|
|
147
|
+
model = self.client.call(
|
|
148
|
+
GraphQLRequest(
|
|
149
|
+
"""
|
|
150
|
+
mutation updateModelGroup($modelGroupId: Int!, $modelTrainingOptions: JSONString) {
|
|
151
|
+
updateModelGroupSettings(
|
|
152
|
+
modelGroupId: $modelGroupId
|
|
153
|
+
modelTrainingOptions: $modelTrainingOptions
|
|
154
|
+
) {
|
|
155
|
+
modelOptions {
|
|
156
|
+
id
|
|
157
|
+
modelTrainingOptions
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
""",
|
|
162
|
+
{
|
|
163
|
+
"modelGroupId": model_group_id,
|
|
164
|
+
"modelTrainingOptions": json.dumps(
|
|
165
|
+
self._parameter_check(model_type, **model_training_options)
|
|
166
|
+
),
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
options = json.loads(
|
|
171
|
+
model["updateModelGroupSettings"]["modelOptions"]["modelTrainingOptions"]
|
|
172
|
+
)
|
|
173
|
+
options["id"] = model["updateModelGroupSettings"]["modelOptions"]["id"]
|
|
174
|
+
return options
|
|
175
|
+
|
|
176
|
+
def _parameter_check(self, model_type, **model_training_options):
|
|
177
|
+
if model_type == "text_extraction":
|
|
178
|
+
valid_params = TEXT_EXTRACTION_PARAMS
|
|
179
|
+
elif model_type == "text_classification":
|
|
180
|
+
valid_params = TEXT_CLASSIFICATION_PARAMS
|
|
181
|
+
else:
|
|
182
|
+
raise ValueError(f"Invalid model type: {model_type}")
|
|
183
|
+
|
|
184
|
+
for param, value in model_training_options.items():
|
|
185
|
+
if param not in valid_params:
|
|
186
|
+
raise ValueError(f"Invalid parameter for {model_type}: {param}")
|
|
187
|
+
if callable(valid_params[param]) and not valid_params[param](value):
|
|
188
|
+
raise ValueError(f"Invalid value for parameter '{param}': {value}")
|
|
189
|
+
if value not in valid_params[param]:
|
|
190
|
+
raise ValueError(f"Invalid value for parameter '{param}': {value}")
|
|
191
|
+
|
|
192
|
+
return model_training_options
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
|
-
|
|
2
|
+
import io
|
|
3
|
+
from os import PathLike
|
|
4
|
+
from typing import List, Union, Dict
|
|
3
5
|
from indico import IndicoClient, IndicoRequestError
|
|
4
6
|
from indico.queries import (
|
|
5
7
|
Submission,
|
|
@@ -42,17 +44,25 @@ class Workflow(IndicoWrapper):
|
|
|
42
44
|
return self.client.call(GetWorkflow(workflow_id))
|
|
43
45
|
|
|
44
46
|
def submit_documents_to_workflow(
|
|
45
|
-
self,
|
|
47
|
+
self,
|
|
48
|
+
workflow_id: int,
|
|
49
|
+
*,
|
|
50
|
+
files: Union[List[Union[str, PathLike]], None] = None,
|
|
51
|
+
streams: Union[Dict[str, io.BufferedIOBase], None] = None,
|
|
46
52
|
) -> List[int]:
|
|
47
53
|
"""
|
|
48
54
|
Args:
|
|
49
55
|
workflow_id (int): Workflow to submit to
|
|
50
56
|
pdf_filepaths (List[str]): Path to local documents you would like to submit
|
|
57
|
+
streams (Dict[str, io.BufferedIOBase]): List of filename keys mapped to streams
|
|
58
|
+
for upload.
|
|
51
59
|
Returns:
|
|
52
60
|
List[int]: List of unique and persistent identifier for each submission.
|
|
53
61
|
"""
|
|
54
62
|
return self.client.call(
|
|
55
|
-
WorkflowSubmission(
|
|
63
|
+
WorkflowSubmission(
|
|
64
|
+
workflow_id=workflow_id, files=files, streams=streams
|
|
65
|
+
)
|
|
56
66
|
)
|
|
57
67
|
|
|
58
68
|
def get_ondoc_ocr_from_etl_url(self, etl_url: str) -> OnDoc:
|
|
@@ -160,9 +170,19 @@ class Workflow(IndicoWrapper):
|
|
|
160
170
|
return self.get_storage_object(job.result)
|
|
161
171
|
|
|
162
172
|
def submit_submission_review(
|
|
163
|
-
self,
|
|
173
|
+
self,
|
|
174
|
+
submission_id: int,
|
|
175
|
+
updated_predictions: dict,
|
|
176
|
+
wait: bool = True,
|
|
177
|
+
force_complete: bool = False,
|
|
164
178
|
):
|
|
165
|
-
job = self.client.call(
|
|
179
|
+
job = self.client.call(
|
|
180
|
+
SubmitReview(
|
|
181
|
+
submission_id,
|
|
182
|
+
changes=updated_predictions,
|
|
183
|
+
force_complete=force_complete,
|
|
184
|
+
)
|
|
185
|
+
)
|
|
166
186
|
if wait:
|
|
167
187
|
job = self.client.call(JobStatus(job.id, wait=True))
|
|
168
188
|
return job
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List
|
|
2
2
|
|
|
3
|
-
from indico_toolkit import
|
|
3
|
+
from indico_toolkit import ToolkitInputError
|
|
4
4
|
|
|
5
5
|
from .predictions import Predictions
|
|
6
|
-
from .extractions import Extractions
|
|
7
|
-
from .classification import Classification, ClassificationMGP
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class WorkflowResult:
|
|
@@ -22,10 +22,10 @@ requires = [
|
|
|
22
22
|
|
|
23
23
|
[tool.flit.metadata.requires-extra]
|
|
24
24
|
test = [
|
|
25
|
-
"pytest>=
|
|
25
|
+
"pytest>=6.2.5",
|
|
26
26
|
"requests-mock>=1.7.0-7",
|
|
27
27
|
"pytest-dependency==0.5.1"
|
|
28
28
|
]
|
|
29
29
|
full = [
|
|
30
|
-
"
|
|
30
|
+
"spacy>=3.1.4,<4"
|
|
31
31
|
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from indico_toolkit.association import split_prediction
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_split_prediction_default():
|
|
7
|
+
prediction = {"text": "a\nb\n\nc", "start": 10, "end": 16}
|
|
8
|
+
assert list(split_prediction(prediction)) == [
|
|
9
|
+
{"text": "a", "start": 10, "end": 11},
|
|
10
|
+
{"text": "b", "start": 12, "end": 13},
|
|
11
|
+
{"text": "c", "start": 15, "end": 16},
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_split_prediction_no_splits():
|
|
16
|
+
prediction = {"text": "no line breaks", "start": 0, "end": 14}
|
|
17
|
+
assert list(split_prediction(prediction)) == [prediction]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_split_prediction_regex():
|
|
21
|
+
prediction = {"text": "a, a ,, b", "start": 10, "end": 19}
|
|
22
|
+
assert list(split_prediction(prediction, r"[,\s]+")) == [
|
|
23
|
+
{"text": "a", "start": 10, "end": 11},
|
|
24
|
+
{"text": "a", "start": 13, "end": 14},
|
|
25
|
+
{"text": "b", "start": 18, "end": 19},
|
|
26
|
+
]
|