indico_toolkit 6.14.1__tar.gz → 7.2.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 (91) hide show
  1. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/PKG-INFO +1 -1
  2. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/__init__.py +1 -1
  3. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/client.py +1 -1
  4. indico_toolkit-7.2.0/indico_toolkit/etloutput/__init__.py +157 -0
  5. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/etloutput/cell.py +1 -1
  6. indico_toolkit-7.2.0/indico_toolkit/etloutput/errors.py +16 -0
  7. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/etloutput/etloutput.py +27 -27
  8. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/etloutput/range.py +1 -1
  9. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/etloutput/table.py +1 -1
  10. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/etloutput/token.py +1 -1
  11. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/indico_wrapper/__init__.py +0 -6
  12. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/ocr/__init__.py +0 -2
  13. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/polling/autoreview.py +6 -6
  14. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/polling/downstream.py +4 -4
  15. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/polling/queries.py +3 -3
  16. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/__init__.py +28 -28
  17. indico_toolkit-7.2.0/indico_toolkit/results/document.py +57 -0
  18. indico_toolkit-7.2.0/indico_toolkit/results/normalization.py +82 -0
  19. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictionlist.py +82 -95
  20. indico_toolkit-7.2.0/indico_toolkit/results/predictions/__init__.py +64 -0
  21. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/box.py +4 -1
  22. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/citation.py +4 -4
  23. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/classification.py +5 -11
  24. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/documentextraction.py +10 -74
  25. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/formextraction.py +15 -20
  26. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/group.py +9 -1
  27. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/prediction.py +4 -10
  28. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/span.py +1 -1
  29. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/summarization.py +38 -13
  30. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/unbundling.py +18 -13
  31. indico_toolkit-7.2.0/indico_toolkit/results/result.py +136 -0
  32. indico_toolkit-7.2.0/indico_toolkit/results/task.py +32 -0
  33. indico_toolkit-7.2.0/indico_toolkit/results/utils.py +101 -0
  34. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/pyproject.toml +1 -1
  35. indico_toolkit-6.14.1/indico_toolkit/auto_populate/__init__.py +0 -3
  36. indico_toolkit-6.14.1/indico_toolkit/auto_populate/populator.py +0 -362
  37. indico_toolkit-6.14.1/indico_toolkit/auto_populate/types.py +0 -66
  38. indico_toolkit-6.14.1/indico_toolkit/etloutput/__init__.py +0 -210
  39. indico_toolkit-6.14.1/indico_toolkit/etloutput/errors.py +0 -16
  40. indico_toolkit-6.14.1/indico_toolkit/indico_wrapper/dataset.py +0 -114
  41. indico_toolkit-6.14.1/indico_toolkit/indico_wrapper/doc_extraction.py +0 -79
  42. indico_toolkit-6.14.1/indico_toolkit/indico_wrapper/reviewer.py +0 -111
  43. indico_toolkit-6.14.1/indico_toolkit/ocr/customocr_object.py +0 -42
  44. indico_toolkit-6.14.1/indico_toolkit/results/document.py +0 -78
  45. indico_toolkit-6.14.1/indico_toolkit/results/model.py +0 -60
  46. indico_toolkit-6.14.1/indico_toolkit/results/normalization.py +0 -159
  47. indico_toolkit-6.14.1/indico_toolkit/results/predictions/__init__.py +0 -88
  48. indico_toolkit-6.14.1/indico_toolkit/results/result.py +0 -152
  49. indico_toolkit-6.14.1/indico_toolkit/results/utils.py +0 -73
  50. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/LICENSE +0 -0
  51. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/README.md +0 -0
  52. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/association/__init__.py +0 -0
  53. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/association/association.py +0 -0
  54. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/association/extracted_tokens.py +0 -0
  55. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/association/line_items.py +0 -0
  56. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/association/positioning.py +0 -0
  57. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/association/splitting.py +0 -0
  58. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/auto_review/__init__.py +0 -0
  59. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/auto_review/auto_review_functions.py +0 -0
  60. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/auto_review/auto_reviewer.py +0 -0
  61. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/errors.py +0 -0
  62. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/indico_wrapper/download.py +0 -0
  63. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/indico_wrapper/indico_wrapper.py +0 -0
  64. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/indico_wrapper/modelop.py +0 -0
  65. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/indico_wrapper/workflow.py +0 -0
  66. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/metrics/__init__.py +0 -0
  67. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/metrics/compare_ground_truth.py +0 -0
  68. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/metrics/compare_models.py +0 -0
  69. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/metrics/metrics.py +0 -0
  70. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/metrics/plotting.py +0 -0
  71. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/ocr/ondoc_object.py +0 -0
  72. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/ocr/standard_object.py +0 -0
  73. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/pipelines/__init__.py +0 -0
  74. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/pipelines/file_processing.py +0 -0
  75. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/polling/__init__.py +0 -0
  76. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/py.typed +0 -0
  77. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/errors.py +0 -0
  78. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/predictions/extraction.py +0 -0
  79. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/results/review.py +0 -0
  80. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/retry.py +0 -0
  81. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/snapshots/__init__.py +0 -0
  82. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/snapshots/snapshot.py +0 -0
  83. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/structure/__init__.py +0 -0
  84. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/structure/create_structure.py +0 -0
  85. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/structure/queries.py +0 -0
  86. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/structure/utils.py +0 -0
  87. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/types/__init__.py +0 -0
  88. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/types/classification.py +0 -0
  89. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/types/extractions.py +0 -0
  90. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/types/predictions.py +0 -0
  91. {indico_toolkit-6.14.1 → indico_toolkit-7.2.0}/indico_toolkit/types/workflow_object.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: indico-toolkit
3
- Version: 6.14.1
3
+ Version: 7.2.0
4
4
  Summary: Classes, functions, and abstractions for Indico IPA
5
5
  License: MIT
6
6
  Author: Indico Solutions Engineering
@@ -21,4 +21,4 @@ __all__ = (
21
21
  "ToolkitStaggeredLoopError",
22
22
  "ToolkitStatusError",
23
23
  )
24
- __version__ = "6.14.1"
24
+ __version__ = "7.2.0"
@@ -8,7 +8,7 @@ from .retry import retry
8
8
 
9
9
 
10
10
  @retry(IndicoRequestError, ConnectionError)
11
- def create_client(
11
+ def create_client( # type: ignore[no-any-unimported]
12
12
  host: str,
13
13
  api_token_path: "str | None" = None,
14
14
  api_token_string: "str | None" = None,
@@ -0,0 +1,157 @@
1
+ from typing import TYPE_CHECKING, TypeAlias, TypeVar
2
+
3
+ from ..results import NULL_BOX, NULL_SPAN, Box, Span
4
+ from ..results.utils import get, has, json_loaded, str_decoded
5
+ from .cell import Cell, CellType
6
+ from .errors import EtlOutputError, TableCellNotFoundError, TokenNotFoundError
7
+ from .etloutput import EtlOutput
8
+ from .range import Range
9
+ from .table import Table
10
+ from .token import Token
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Awaitable, Callable
14
+
15
+ __all__ = (
16
+ "Box",
17
+ "Cell",
18
+ "CellType",
19
+ "EtlOutput",
20
+ "EtlOutputError",
21
+ "load",
22
+ "load_async",
23
+ "NULL_BOX",
24
+ "NULL_SPAN",
25
+ "Range",
26
+ "Span",
27
+ "Table",
28
+ "TableCellNotFoundError",
29
+ "Token",
30
+ "TokenNotFoundError",
31
+ )
32
+
33
+ Loadable: TypeAlias = "dict[str, object] | list[object] | str | bytes"
34
+ Readable = TypeVar("Readable")
35
+ URI: TypeAlias = str
36
+
37
+
38
+ def load(
39
+ etl_output: "Loadable | Readable",
40
+ *,
41
+ reader: "Callable[[Readable | URI], Loadable]",
42
+ text: bool = True,
43
+ tokens: bool = True,
44
+ tables: bool = True,
45
+ ) -> EtlOutput:
46
+ """
47
+ Load `etl_output` as an `EtlOutput` dataclass.
48
+
49
+ `etl_output` can be a dict, JSON string/bytes, or something that can be read by
50
+ `reader` to produce a loadable type.
51
+
52
+ `reader` must be able to load Indico storage URIs as lists or JSON strings/bytes.
53
+
54
+ Use `text`, `tokens`, and `tables` to specify what not to load.
55
+
56
+ ```
57
+ result = results.load(submission.result_file, reader=read_uri)
58
+ etl_outputs = {
59
+ document: etloutput.load(document.etl_output_uri, reader=read_uri)
60
+ for document in result.documents
61
+ if not document.failed
62
+ }
63
+ ```
64
+ """
65
+ if not isinstance(etl_output, dict):
66
+ if (isinstance(etl_output, str) and etl_output.startswith("{")) or (
67
+ isinstance(etl_output, bytes) and etl_output.startswith(b"{")
68
+ ):
69
+ etl_output = json_loaded(etl_output)
70
+ else:
71
+ etl_output = json_loaded(reader(etl_output)) # type: ignore[arg-type]
72
+
73
+ pages = get(etl_output, list, "pages")
74
+
75
+ if text and has(pages, str, 0, "text"):
76
+ text_pages = map(
77
+ lambda page: str_decoded(reader(get(page, str, "text"))), pages # type: ignore[arg-type]
78
+ )
79
+ else:
80
+ text_pages = () # type: ignore[assignment]
81
+
82
+ if tokens and has(pages, str, 0, "tokens"):
83
+ token_dict_pages = map(
84
+ lambda page: json_loaded(reader(get(page, str, "tokens"))), pages
85
+ )
86
+ else:
87
+ token_dict_pages = () # type: ignore[assignment]
88
+
89
+ if tables and has(pages, str, 0, "tables"):
90
+ table_dict_pages = map(
91
+ lambda page: json_loaded(reader(get(page, str, "tables"))), pages
92
+ )
93
+ else:
94
+ table_dict_pages = () # type: ignore[assignment]
95
+
96
+ return EtlOutput.from_pages(text_pages, token_dict_pages, table_dict_pages)
97
+
98
+
99
+ async def load_async(
100
+ etl_output: "Loadable | Readable",
101
+ *,
102
+ reader: "Callable[[Readable | URI], Awaitable[Loadable]]",
103
+ text: bool = True,
104
+ tokens: bool = True,
105
+ tables: bool = True,
106
+ ) -> EtlOutput:
107
+ """
108
+ Load `etl_output` as an `EtlOutput` dataclass.
109
+
110
+ `etl_output` can be a dict, JSON string/bytes, or something that can be read by
111
+ `reader` to produce a loadable type.
112
+
113
+ `reader` must be able to load Indico storage URIs as lists or JSON strings/bytes.
114
+
115
+ Use `text`, `tokens`, and `tables` to specify what not to load.
116
+
117
+ ```
118
+ result = await results.load_async(submission.result_file, reader=read_uri)
119
+ etl_outputs = {
120
+ document: await etloutput.load_async(document.etl_output_uri, reader=read_uri)
121
+ for document in result.documents
122
+ if not document.failed
123
+ }
124
+ ```
125
+ """
126
+ if not isinstance(etl_output, dict):
127
+ if (isinstance(etl_output, str) and etl_output.startswith("{")) or (
128
+ isinstance(etl_output, bytes) and etl_output.startswith(b"{")
129
+ ):
130
+ etl_output = json_loaded(etl_output)
131
+ else:
132
+ etl_output = json_loaded(await reader(etl_output)) # type: ignore[arg-type]
133
+
134
+ pages = get(etl_output, list, "pages")
135
+
136
+ if text and has(pages, str, 0, "text"):
137
+ text_pages = [
138
+ str_decoded(await reader(get(page, str, "text"))) for page in pages # type: ignore[arg-type]
139
+ ]
140
+ else:
141
+ text_pages = () # type: ignore[assignment]
142
+
143
+ if tokens and has(pages, str, 0, "tokens"):
144
+ token_dict_pages = [
145
+ json_loaded(await reader(get(page, str, "tokens"))) for page in pages
146
+ ]
147
+ else:
148
+ token_dict_pages = () # type: ignore[assignment]
149
+
150
+ if tables and has(pages, str, 0, "tables"):
151
+ table_dict_pages = [
152
+ json_loaded(await reader(get(page, str, "tables"))) for page in pages
153
+ ]
154
+ else:
155
+ table_dict_pages = () # type: ignore[assignment]
156
+
157
+ return EtlOutput.from_pages(text_pages, token_dict_pages, table_dict_pages)
@@ -31,7 +31,7 @@ class Cell:
31
31
  @staticmethod
32
32
  def from_dict(cell: object, page: int) -> "Cell":
33
33
  """
34
- Create a `Cell` from a v1 or v3 cell dictionary.
34
+ Create a `Cell` from a cell dictionary.
35
35
  """
36
36
  get(cell, dict, "position")["page_num"] = page
37
37
 
@@ -0,0 +1,16 @@
1
+ class EtlOutputError(Exception):
2
+ """
3
+ Raised when an error occurs accessing `EtlOutput` values.
4
+ """
5
+
6
+
7
+ class TokenNotFoundError(EtlOutputError):
8
+ """
9
+ Raised when a `Token` can't be found for a `Span`.
10
+ """
11
+
12
+
13
+ class TableCellNotFoundError(EtlOutputError):
14
+ """
15
+ Raised when a `Table` and `Cell` can't be found for a `Token`.
16
+ """
@@ -28,30 +28,30 @@ class EtlOutput:
28
28
 
29
29
  @staticmethod
30
30
  def from_pages(
31
- text_by_page: "Iterable[str]",
32
- token_dicts_by_page: "Iterable[Iterable[object]]",
33
- table_dicts_by_page: "Iterable[Iterable[object]]",
31
+ text_pages: "Iterable[str]",
32
+ token_dict_pages: "Iterable[Iterable[object]]",
33
+ table_dict_pages: "Iterable[Iterable[object]]",
34
34
  ) -> "EtlOutput":
35
35
  """
36
- Create an `EtlOutput` from v1 or v3 page lists.
36
+ Create an `EtlOutput` from pages of text, tokens, and tables.
37
37
  """
38
- text_by_page = tuple(text_by_page)
39
- tokens_by_page = tuple(
38
+ text_pages = tuple(text_pages)
39
+ token_pages = tuple(
40
40
  tuple(sorted(map(Token.from_dict, token_dict_page), key=attrgetter("span")))
41
- for token_dict_page in token_dicts_by_page
41
+ for token_dict_page in token_dict_pages
42
42
  )
43
- tables_by_page = tuple(
43
+ table_pages = tuple(
44
44
  tuple(sorted(map(Table.from_dict, table_dict_page), key=attrgetter("box")))
45
- for table_dict_page in table_dicts_by_page
45
+ for table_dict_page in table_dict_pages
46
46
  )
47
47
 
48
48
  return EtlOutput(
49
- text="\n".join(text_by_page),
50
- text_on_page=text_by_page,
51
- tokens=tuple(itertools.chain.from_iterable(tokens_by_page)),
52
- tokens_on_page=tokens_by_page,
53
- tables=tuple(itertools.chain.from_iterable(tables_by_page)),
54
- tables_on_page=tables_by_page,
49
+ text="\n".join(text_pages),
50
+ text_on_page=text_pages,
51
+ tokens=tuple(itertools.chain.from_iterable(token_pages)),
52
+ tokens_on_page=token_pages,
53
+ tables=tuple(itertools.chain.from_iterable(table_pages)),
54
+ tables_on_page=table_pages,
55
55
  )
56
56
 
57
57
  def token_for(self, span: Span) -> Token:
@@ -64,21 +64,21 @@ class EtlOutput:
64
64
  first = bisect_right(tokens, span.start, key=attrgetter("span.end"))
65
65
  last = bisect_left(tokens, span.end, lo=first, key=attrgetter("span.start"))
66
66
  tokens = tokens[first:last]
67
-
68
- return Token(
69
- text=self.text[span.slice],
70
- span=span,
71
- box=Box(
72
- page=min(token.box.page for token in tokens),
73
- top=min(token.box.top for token in tokens),
74
- left=min(token.box.left for token in tokens),
75
- right=max(token.box.right for token in tokens),
76
- bottom=max(token.box.bottom for token in tokens),
77
- ),
78
- )
79
67
  except (IndexError, ValueError) as error:
80
68
  raise TokenNotFoundError(f"no token contains {span!r}") from error
81
69
 
70
+ return Token(
71
+ text=self.text[span.slice],
72
+ box=Box(
73
+ page=span.page,
74
+ top=min(token.box.top for token in tokens),
75
+ left=min(token.box.left for token in tokens),
76
+ right=max(token.box.right for token in tokens),
77
+ bottom=max(token.box.bottom for token in tokens),
78
+ ),
79
+ span=span,
80
+ )
81
+
82
82
  def table_cell_for(self, token: Token) -> "tuple[Table, Cell]":
83
83
  """
84
84
  Return the `Table` and `Cell` that contain the midpoint of `token`.
@@ -15,7 +15,7 @@ class Range:
15
15
  @staticmethod
16
16
  def from_dict(cell: object) -> "Range":
17
17
  """
18
- Create a `Range` from a v1 or v3 cell dictionary.
18
+ Create a `Range` from a cell dictionary.
19
19
  """
20
20
  rows = get(cell, list, "rows")
21
21
  columns = get(cell, list, "columns")
@@ -16,7 +16,7 @@ class Table:
16
16
  @staticmethod
17
17
  def from_dict(table: object) -> "Table":
18
18
  """
19
- Create a `Table` from a v1 or v3 table dictionary.
19
+ Create a `Table` from a table dictionary.
20
20
  """
21
21
  page = get(table, int, "page_num")
22
22
  get(table, dict, "position")["page_num"] = page
@@ -13,7 +13,7 @@ class Token:
13
13
  @staticmethod
14
14
  def from_dict(token: object) -> "Token":
15
15
  """
16
- Create a `Token` from a v1 or v3 token dictionary.
16
+ Create a `Token` from a token dictionary.
17
17
  """
18
18
  get(token, dict, "position")["page_num"] = get(token, int, "page_num")
19
19
  get(token, dict, "doc_offset")["page_num"] = get(token, int, "page_num")
@@ -1,15 +1,9 @@
1
- from .dataset import Datasets
2
- from .doc_extraction import DocExtraction
3
1
  from .download import Download
4
2
  from .indico_wrapper import IndicoWrapper
5
- from .reviewer import Reviewer
6
3
  from .workflow import Workflow
7
4
 
8
5
  __all__ = (
9
- "Datasets",
10
- "DocExtraction",
11
6
  "Download",
12
7
  "IndicoWrapper",
13
- "Reviewer",
14
8
  "Workflow",
15
9
  )
@@ -1,9 +1,7 @@
1
- from .customocr_object import CustomOcr
2
1
  from .ondoc_object import OnDoc
3
2
  from .standard_object import StandardOcr
4
3
 
5
4
  __all__ = (
6
- "CustomOcr",
7
5
  "OnDoc",
8
6
  "StandardOcr",
9
7
  )
@@ -3,8 +3,8 @@ import logging
3
3
  from dataclasses import dataclass
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- from indico import AsyncIndicoClient, IndicoConfig # type: ignore[import-untyped]
7
- from indico.queries import ( # type: ignore[import-untyped]
6
+ from indico import AsyncIndicoClient, IndicoConfig
7
+ from indico.queries import (
8
8
  GetSubmission,
9
9
  JobStatus,
10
10
  RetrieveStorageObject,
@@ -41,7 +41,7 @@ class AutoReviewPoller:
41
41
  and submits the review results concurrently.
42
42
  """
43
43
 
44
- def __init__(
44
+ def __init__( # type: ignore[no-any-unimported]
45
45
  self,
46
46
  config: IndicoConfig,
47
47
  workflow_id: int,
@@ -53,7 +53,7 @@ class AutoReviewPoller:
53
53
  load_etl_output: bool = True,
54
54
  load_text: bool = True,
55
55
  load_tokens: bool = True,
56
- load_tables: bool = False,
56
+ load_tables: bool = True,
57
57
  retry_count: int = 4,
58
58
  retry_wait: float = 1,
59
59
  retry_backoff: float = 4,
@@ -96,8 +96,8 @@ class AutoReviewPoller:
96
96
  *(self._reap_workers() for _ in range(self._worker_count)),
97
97
  )
98
98
 
99
- async def _retrieve_storage_object(self, url: str) -> object:
100
- return await self._client_call(RetrieveStorageObject(url))
99
+ async def _retrieve_storage_object(self, uri: str) -> "Any":
100
+ return await self._client_call(RetrieveStorageObject(uri))
101
101
 
102
102
  async def _spawn_workers(self) -> None:
103
103
  """
@@ -2,12 +2,12 @@ import asyncio
2
2
  import logging
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from indico import AsyncIndicoClient, IndicoConfig # type: ignore[import-untyped]
6
- from indico.queries import ( # type: ignore[import-untyped]
5
+ from indico import AsyncIndicoClient, IndicoConfig
6
+ from indico.queries import (
7
7
  GetSubmission,
8
8
  UpdateSubmission,
9
9
  )
10
- from indico.types import Submission # type: ignore[import-untyped]
10
+ from indico.types import Submission
11
11
 
12
12
  from ..retry import retry
13
13
  from .queries import SubmissionIdsPendingDownstream
@@ -29,7 +29,7 @@ class DownstreamPoller:
29
29
  them concurrently, and marks them as retrieved.
30
30
  """
31
31
 
32
- def __init__(
32
+ def __init__( # type: ignore[no-any-unimported]
33
33
  self,
34
34
  config: IndicoConfig,
35
35
  workflow_id: int,
@@ -1,12 +1,12 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from indico.queries import GraphQLRequest # type: ignore[import-untyped]
3
+ from indico.queries import GraphQLRequest
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from typing import Any
7
7
 
8
8
 
9
- class SubmissionIdsPendingAutoReview(GraphQLRequest): # type: ignore[misc]
9
+ class SubmissionIdsPendingAutoReview(GraphQLRequest): # type: ignore[misc, no-any-unimported]
10
10
  QUERY = """
11
11
  query SubmissionIdsPendingAutoReview($workflowIds: [Int]) {
12
12
  submissions(
@@ -33,7 +33,7 @@ class SubmissionIdsPendingAutoReview(GraphQLRequest): # type: ignore[misc]
33
33
  }
34
34
 
35
35
 
36
- class SubmissionIdsPendingDownstream(GraphQLRequest): # type: ignore[misc]
36
+ class SubmissionIdsPendingDownstream(GraphQLRequest): # type: ignore[misc, no-any-unimported]
37
37
  QUERY = """
38
38
  query SubmissionIdsPendingDownstream($workflowIds: [Int]) {
39
39
  submissions(
@@ -1,9 +1,7 @@
1
- import json
2
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, TypeAlias, TypeVar, overload
3
2
 
4
3
  from .document import Document
5
4
  from .errors import ResultError
6
- from .model import ModelGroup, ModelGroupType
7
5
  from .predictionlist import PredictionList
8
6
  from .predictions import (
9
7
  NULL_BOX,
@@ -23,7 +21,8 @@ from .predictions import (
23
21
  )
24
22
  from .result import Result
25
23
  from .review import Review, ReviewType
26
- from .utils import get
24
+ from .task import Task, TaskType
25
+ from .utils import json_loaded
27
26
 
28
27
  if TYPE_CHECKING:
29
28
  from collections.abc import Awaitable, Callable
@@ -40,8 +39,6 @@ __all__ = (
40
39
  "Group",
41
40
  "load",
42
41
  "load_async",
43
- "ModelGroup",
44
- "ModelGroupType",
45
42
  "NULL_BOX",
46
43
  "NULL_CITATION",
47
44
  "NULL_SPAN",
@@ -53,16 +50,24 @@ __all__ = (
53
50
  "ReviewType",
54
51
  "Span",
55
52
  "Summarization",
53
+ "Task",
54
+ "TaskType",
56
55
  "Unbundling",
57
56
  )
58
57
 
58
+ Loadable: TypeAlias = "dict[str, object] | str | bytes"
59
+ Readable = TypeVar("Readable")
59
60
 
61
+
62
+ @overload
63
+ def load(result: Loadable) -> Result: ...
64
+ @overload
65
+ def load(result: Readable, *, reader: "Callable[[Readable], Loadable]") -> Result: ...
60
66
  def load(result: object, *, reader: "Callable[..., object] | None" = None) -> Result:
61
67
  """
62
- Load `result` as a Result dataclass.
68
+ Load `result` as a `Result` dataclass. `result` can be a dict or JSON string/bytes.
63
69
 
64
- `result` can be a dict, a JSON string, or something that can be read with `reader`
65
- to produce either.
70
+ If `reader` is provided, it should read `result` to produce a loadable type.
66
71
 
67
72
  ```
68
73
  for result_file in result_folder.glob("*.json"):
@@ -72,33 +77,28 @@ def load(result: object, *, reader: "Callable[..., object] | None" = None) -> Re
72
77
  if reader:
73
78
  result = reader(result)
74
79
 
75
- return _load(result)
80
+ return Result.from_dict(json_loaded(result))
76
81
 
77
82
 
83
+ @overload
84
+ async def load_async(result: Loadable) -> Result: ...
85
+ @overload
86
+ async def load_async(
87
+ result: Readable, *, reader: "Callable[[Readable], Awaitable[Loadable]]"
88
+ ) -> Result: ...
78
89
  async def load_async(
79
90
  result: object, *, reader: "Callable[..., Awaitable[object]] | None" = None
80
91
  ) -> Result:
81
92
  """
82
- Load `result` as a Result dataclass.
93
+ Load `result` as a `Result` dataclass. `result` can be a dict or JSON string/bytes.
94
+
95
+ If `reader` is provided, it should read `result` to produce a loadable type.
83
96
 
84
- `result` can be a dict, a JSON string, or something that can be read with `reader`
85
- to produce either.
97
+ ```
98
+ result = await results.load_async(submission.result_file, reader=read_uri)
99
+ ```
86
100
  """
87
101
  if reader:
88
102
  result = await reader(result)
89
103
 
90
- return _load(result)
91
-
92
-
93
- def _load(result: object) -> Result:
94
- if isinstance(result, str) and result.strip().startswith("{"):
95
- result = json.loads(result)
96
-
97
- file_version = get(result, int, "file_version")
98
-
99
- if file_version == 1:
100
- return Result.from_v1_dict(result)
101
- elif file_version == 3:
102
- return Result.from_v3_dict(result)
103
- else:
104
- raise ResultError(f"unsupported file version `{file_version!r}`")
104
+ return Result.from_dict(json_loaded(result))
@@ -0,0 +1,57 @@
1
+ from dataclasses import dataclass
2
+
3
+ from .utils import get
4
+
5
+
6
+ @dataclass(frozen=True, order=True)
7
+ class Document:
8
+ id: int
9
+ name: str
10
+ etl_output_uri: str
11
+ failed: bool
12
+ error: str
13
+ traceback: str
14
+
15
+ # Auto review changes must reproduce all model and component sections that were
16
+ # present in the original result file. This may not be possible from the
17
+ # predictions alone--if a model or component had an empty section because it didn't
18
+ # produce predictions or if all of the predictions for that section were dropped.
19
+ # As such, the model and component IDs seen when parsing a result file are tracked
20
+ # per-document so that the empty sections can be reproduced later.
21
+ _model_ids: "frozenset[str]"
22
+ _component_ids: "frozenset[str]"
23
+
24
+ @staticmethod
25
+ def from_dict(document: object) -> "Document":
26
+ """
27
+ Create a `Document` from a document dictionary.
28
+ """
29
+ model_results = get(document, dict, "model_results", "ORIGINAL")
30
+ component_results = get(document, dict, "component_results", "ORIGINAL")
31
+
32
+ return Document(
33
+ id=get(document, int, "submissionfile_id"),
34
+ name=get(document, str, "input_filename"),
35
+ etl_output_uri=get(document, str, "etl_output"),
36
+ failed=False,
37
+ error="",
38
+ traceback="",
39
+ _model_ids=frozenset(model_results.keys()),
40
+ _component_ids=frozenset(component_results.keys()),
41
+ )
42
+
43
+ @staticmethod
44
+ def from_errored_file_dict(errored_file: object) -> "Document":
45
+ """
46
+ Create a `Document` from an errored file dictionary.
47
+ """
48
+ return Document(
49
+ id=get(errored_file, int, "submissionfile_id"),
50
+ name=get(errored_file, str, "input_filename"),
51
+ etl_output_uri="",
52
+ failed=True,
53
+ error=get(errored_file, str, "error"),
54
+ traceback=get(errored_file, str, "traceback"),
55
+ _model_ids=frozenset(),
56
+ _component_ids=frozenset(),
57
+ )