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.
Files changed (155) hide show
  1. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/CHANGELOG.md +7 -0
  2. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/PKG-INFO +2 -9
  3. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/README.md +0 -6
  4. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/__init__.py +1 -1
  5. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/__init__.py +3 -3
  6. indico_toolkit-6.1.0/indico_toolkit/association/splitting.py +41 -0
  7. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_populate/populator.py +43 -38
  8. indico_toolkit-6.1.0/indico_toolkit/indico_wrapper/modelop.py +192 -0
  9. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/workflow.py +25 -5
  10. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/workflow_object.py +2 -4
  11. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/pyproject.toml +2 -2
  12. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/requirements.txt +1 -2
  13. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/setup.py +1 -1
  14. indico_toolkit-6.1.0/tests/association/test_splitting.py +26 -0
  15. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/auto_populate/test_populator.py +0 -1
  16. indico_toolkit-6.1.0/tests/indico_wrapper/test_modelops.py +67 -0
  17. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/pipelines/test_file_processing.py +1 -1
  18. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_worflow_result.py +14 -9
  19. indico_toolkit-6.0.1/indico_toolkit/association/split_merged_values.py +0 -50
  20. indico_toolkit-6.0.1/indico_toolkit/highlighter/__init__.py +0 -1
  21. indico_toolkit-6.0.1/indico_toolkit/highlighter/highlighter.py +0 -306
  22. indico_toolkit-6.0.1/indico_toolkit/pipelines/pdf_manipulation.py +0 -67
  23. indico_toolkit-6.0.1/indico_toolkit/staggered_loop/__init__.py +0 -2
  24. indico_toolkit-6.0.1/indico_toolkit/staggered_loop/metrics.py +0 -1433
  25. indico_toolkit-6.0.1/indico_toolkit/staggered_loop/queries.py +0 -42
  26. indico_toolkit-6.0.1/indico_toolkit/staggered_loop/staggered_loop.py +0 -724
  27. indico_toolkit-6.0.1/indico_toolkit/staggered_loop/staggered_loop_recruiter.py +0 -180
  28. indico_toolkit-6.0.1/tests/association/test_split_merged_values.py +0 -35
  29. indico_toolkit-6.0.1/tests/highlighter/data/fcc_doc.pdf +0 -0
  30. indico_toolkit-6.0.1/tests/highlighter/data/fcc_ocr_obj.p +0 -0
  31. indico_toolkit-6.0.1/tests/highlighter/data/fcc_predictions.p +0 -0
  32. indico_toolkit-6.0.1/tests/highlighter/data/highlighted_fcc.pdf +0 -0
  33. indico_toolkit-6.0.1/tests/highlighter/data/highlighted_invoice.pdf +0 -0
  34. indico_toolkit-6.0.1/tests/highlighter/data/invoice_sample.pdf +0 -0
  35. indico_toolkit-6.0.1/tests/highlighter/data/ocr_obj.p +0 -0
  36. indico_toolkit-6.0.1/tests/highlighter/data/predictions.p +0 -0
  37. indico_toolkit-6.0.1/tests/highlighter/test_highlighter.py +0 -195
  38. indico_toolkit-6.0.1/tests/integration/test_processing_pdfs.py +0 -22
  39. indico_toolkit-6.0.1/tests/pipelines/test_pdf_manipulation.py +0 -25
  40. indico_toolkit-6.0.1/tests/staggered_loop/__init__.py +0 -0
  41. indico_toolkit-6.0.1/tests/staggered_loop/test_staggered_loop.py +0 -162
  42. indico_toolkit-6.0.1/tests/staggered_loop/test_staggered_loop_recruiter.py +0 -24
  43. indico_toolkit-6.0.1/tests/types/__init__.py +0 -0
  44. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/.github/workflows/build.yml +0 -0
  45. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/.gitignore +0 -0
  46. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/LICENSE +0 -0
  47. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/auto_review_predictions.py +0 -0
  48. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/copy_teach_task.py +0 -0
  49. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/create_auto_classification_workflow.py +0 -0
  50. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/create_full_structure.py +0 -0
  51. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/dataset_tasks.py +0 -0
  52. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/ground_truth_metrics.py +0 -0
  53. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/manipulate_pdfs.py +0 -0
  54. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/merge_snapshots.py +0 -0
  55. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/model_metrics.py +0 -0
  56. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/pdf_highlighter.py +0 -0
  57. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/row_association.py +0 -0
  58. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/submitting_to_doc_extraction.py +0 -0
  59. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/examples/submitting_to_workflow.py +0 -0
  60. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/association.py +0 -0
  61. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/extracted_tokens.py +0 -0
  62. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/line_items.py +0 -0
  63. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/association/positioning.py +0 -0
  64. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_populate/__init__.py +0 -0
  65. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_populate/types.py +0 -0
  66. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_review/__init__.py +0 -0
  67. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_review/auto_review_functions.py +0 -0
  68. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/auto_review/auto_reviewer.py +0 -0
  69. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/client.py +0 -0
  70. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/errors.py +0 -0
  71. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/__init__.py +0 -0
  72. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/dataset.py +0 -0
  73. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/doc_extraction.py +0 -0
  74. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/download.py +0 -0
  75. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/indico_wrapper.py +0 -0
  76. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/indico_wrapper/reviewer.py +0 -0
  77. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/__init__.py +0 -0
  78. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/compare_ground_truth.py +0 -0
  79. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/compare_models.py +0 -0
  80. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/metrics.py +0 -0
  81. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/metrics/plotting.py +0 -0
  82. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/__init__.py +0 -0
  83. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/customocr_object.py +0 -0
  84. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/ondoc_object.py +0 -0
  85. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/ocr/standard_object.py +0 -0
  86. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/pipelines/__init__.py +0 -0
  87. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/pipelines/file_processing.py +0 -0
  88. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/retry.py +0 -0
  89. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/snapshots/__init__.py +0 -0
  90. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/snapshots/snapshot.py +0 -0
  91. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/__init__.py +0 -0
  92. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/create_structure.py +0 -0
  93. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/queries.py +0 -0
  94. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/structure/utils.py +0 -0
  95. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/__init__.py +0 -0
  96. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/classification.py +0 -0
  97. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/extractions.py +0 -0
  98. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/indico_toolkit/types/predictions.py +0 -0
  99. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/__init__.py +0 -0
  100. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/__init__.py +0 -0
  101. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/conftest.py +0 -0
  102. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/three_row_invoice/preds.json +0 -0
  103. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/three_row_invoice/tokens.json +0 -0
  104. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/token_page/tokens.json +0 -0
  105. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/two_row_bank_statement/preds.json +0 -0
  106. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/data/two_row_bank_statement/tokens.json +0 -0
  107. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/test_extracted_tokens.py +0 -0
  108. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/test_line_items.py +0 -0
  109. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/association/test_positioning.py +0 -0
  110. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/auto_review/__init__.py +0 -0
  111. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/auto_review/test_auto_review.py +0 -0
  112. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/conftest.py +0 -0
  113. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/auto_class/class_a/sample_two_pages.pdf +0 -0
  114. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/auto_class/class_b/inv_sample2.pdf +0 -0
  115. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/auto_review/preds.json +0 -0
  116. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc.pdf +0 -0
  117. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc_classification.json +0 -0
  118. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc_result.json +0 -0
  119. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/fin_disc_snapshot.csv +0 -0
  120. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/samples/staggered_loop_recruiter_example.json +0 -0
  121. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/snapshots/populator_snapshot.csv +0 -0
  122. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/snapshots/snapshot.csv +0 -0
  123. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/data/snapshots/updated_snapshot.csv +0 -0
  124. {indico_toolkit-6.0.1/tests/highlighter → indico_toolkit-6.1.0/tests/indico_wrapper}/__init__.py +0 -0
  125. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_dataset.py +0 -0
  126. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_doc_extraction.py +0 -0
  127. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_download.py +0 -0
  128. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_indico_wrapper.py +0 -0
  129. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_reviewer.py +0 -0
  130. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/indico_wrapper/test_workflow.py +0 -0
  131. {indico_toolkit-6.0.1/tests/indico_wrapper → indico_toolkit-6.1.0/tests/integration}/__init__.py +0 -0
  132. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/integration/test_snapshot_upload.py +0 -0
  133. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/integration/test_workflow_rows.py +0 -0
  134. {indico_toolkit-6.0.1/tests/integration → indico_toolkit-6.1.0/tests/metrics}/__init__.py +0 -0
  135. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_compare_ground_truth.py +0 -0
  136. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_compare_models.py +0 -0
  137. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_metrics.py +0 -0
  138. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/metrics/test_plotting.py +0 -0
  139. {indico_toolkit-6.0.1/tests/metrics → indico_toolkit-6.1.0/tests/ocr}/__init__.py +0 -0
  140. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/ocr/test_customocr_object.py +0 -0
  141. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/ocr/test_ondoc_object.py +0 -0
  142. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/ocr/test_standard_object.py +0 -0
  143. {indico_toolkit-6.0.1/tests/ocr → indico_toolkit-6.1.0/tests/pipelines}/__init__.py +0 -0
  144. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/pytest.ini +0 -0
  145. {indico_toolkit-6.0.1/tests/pipelines → indico_toolkit-6.1.0/tests/snapshots}/__init__.py +0 -0
  146. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/snapshots/conftest.py +0 -0
  147. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/snapshots/test_snapshot.py +0 -0
  148. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/structure/test_create_structure.py +0 -0
  149. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/test_create_client.py +0 -0
  150. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/test_retry.py +0 -0
  151. {indico_toolkit-6.0.1/tests/snapshots → indico_toolkit-6.1.0/tests/types}/__init__.py +0 -0
  152. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/conftest.py +0 -0
  153. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_classifications.py +0 -0
  154. {indico_toolkit-6.0.1 → indico_toolkit-6.1.0}/tests/types/test_extractions.py +0 -0
  155. {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.1
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>=5.2.1 ; extra == "test"
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
1
  """A package to support Indico IPA development"""
2
- __version__ = "6.0.1"
2
+ __version__ = "6.1.0"
3
3
 
4
4
  from .errors import *
5
5
  from .client import create_client
@@ -1,5 +1,5 @@
1
- from .line_items import LineItems
1
+ from .association import Association, sequences_exact, sequences_overlap
2
2
  from .extracted_tokens import ExtractedTokens
3
- from .split_merged_values import split_prediction_into_many
3
+ from .line_items import LineItems
4
4
  from .positioning import Positioning
5
- from .association import Association, sequences_exact, sequences_overlap
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 typing import List, Tuple, Dict
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
- "png",
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
- self._fp.get_file_paths_from_dir(
77
- directory_path, accepted_types=accepted_types, recursive_search=True
78
- )
79
- file_paths = self._fp.file_paths
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
- classes = [value[0]["label"] for value in file_to_targets.values()]
88
- # Create empty dataset
89
- optional_ocr_options = {
90
- "auto_rotate": False,
91
- "upscale_images": True,
92
- "languages": ["ENG"],
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=file_paths,
99
+ files_to_upload=files,
97
100
  read_api=True,
98
101
  single_column=False,
99
- **optional_ocr_options,
102
+ auto_rotate=False,
103
+ upscale_images=True,
104
+ languages=["ENG"],
100
105
  )
101
- # Create workflow
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=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, target_name_map = self._get_teach_task_details(
118
+ labelset_id, model_group_id, label_map = self._get_teach_task_details(
114
119
  teach_task_id
115
120
  )
116
- if len(classes) < 2:
117
- raise ToolkitPopulationError(
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, labels=[dataclasses.asdict(label) for label in labels], model_group_id=model_group_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
- from typing import List, Union
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, workflow_id: int, pdf_filepaths: List[str]
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(workflow_id=workflow_id, files=pdf_filepaths)
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, submission_id: int, updated_predictions: dict, wait: bool = True
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(SubmitReview(submission_id, changes=updated_predictions))
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, Union
1
+ from typing import List
2
2
 
3
- from indico_toolkit import ToolkitStatusError, ToolkitInputError
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>=5.2.1",
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
- "PyMuPDF==1.19.6", "spacy>=3.1.4,<4"
30
+ "spacy>=3.1.4,<4"
31
31
  ]
@@ -1,8 +1,7 @@
1
1
  indico-client>=5.1.4
2
2
  python-dateutil==2.8.1
3
- PyMuPDF==1.19.6
4
3
  pytz==2021.1
5
- pytest==6.2.2
4
+ pytest==6.2.5
6
5
  pytest-dependency==0.5.1
7
6
  black==22.3
8
7
  plotly==5.2.1
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="indico-toolkit",
5
- version="6.0.1",
5
+ version="6.1.0",
6
6
  packages=find_packages(exclude=["tests"]),
7
7
  description="""Tools to assist with Indico IPA development""",
8
8
  license="MIT License (See LICENSE)",
@@ -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
+ ]
@@ -27,7 +27,6 @@ def test_create_classification_workflow(indico_client, testdir_file_path):
27
27
  "My workflow",
28
28
  "My teach task",
29
29
  )
30
- assert len(auto_populator._fp.file_paths) == 2
31
30
  assert isinstance(new_workflow, Workflow)
32
31
 
33
32