indico_toolkit 6.14.0__tar.gz → 6.14.2__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 (83) hide show
  1. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/PKG-INFO +6 -6
  2. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/README.md +5 -5
  3. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/__init__.py +1 -1
  4. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/client.py +1 -1
  5. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/__init__.py +1 -1
  6. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/cell.py +1 -1
  7. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/range.py +1 -1
  8. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/table.py +1 -1
  9. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/token.py +1 -1
  10. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/__init__.py +0 -6
  11. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/ocr/__init__.py +0 -2
  12. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/polling/autoreview.py +3 -3
  13. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/polling/downstream.py +4 -4
  14. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/polling/queries.py +3 -3
  15. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/__init__.py +2 -2
  16. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/document.py +13 -7
  17. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/model.py +1 -1
  18. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/normalization.py +15 -4
  19. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictionlist.py +23 -10
  20. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/__init__.py +2 -2
  21. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/box.py +1 -1
  22. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/citation.py +1 -1
  23. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/classification.py +1 -1
  24. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/documentextraction.py +9 -5
  25. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/formextraction.py +16 -11
  26. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/group.py +10 -2
  27. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/span.py +1 -1
  28. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/summarization.py +1 -1
  29. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/unbundling.py +12 -7
  30. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/result.py +58 -7
  31. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/review.py +1 -1
  32. indico_toolkit-6.14.0/indico_toolkit/results/utilities.py → indico_toolkit-6.14.2/indico_toolkit/results/utils.py +19 -17
  33. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/pyproject.toml +4 -2
  34. indico_toolkit-6.14.0/indico_toolkit/auto_populate/__init__.py +0 -3
  35. indico_toolkit-6.14.0/indico_toolkit/auto_populate/populator.py +0 -362
  36. indico_toolkit-6.14.0/indico_toolkit/auto_populate/types.py +0 -66
  37. indico_toolkit-6.14.0/indico_toolkit/indico_wrapper/dataset.py +0 -114
  38. indico_toolkit-6.14.0/indico_toolkit/indico_wrapper/doc_extraction.py +0 -79
  39. indico_toolkit-6.14.0/indico_toolkit/indico_wrapper/reviewer.py +0 -111
  40. indico_toolkit-6.14.0/indico_toolkit/ocr/customocr_object.py +0 -42
  41. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/LICENSE +0 -0
  42. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/association/__init__.py +0 -0
  43. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/association/association.py +0 -0
  44. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/association/extracted_tokens.py +0 -0
  45. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/association/line_items.py +0 -0
  46. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/association/positioning.py +0 -0
  47. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/association/splitting.py +0 -0
  48. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/auto_review/__init__.py +0 -0
  49. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/auto_review/auto_review_functions.py +0 -0
  50. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/auto_review/auto_reviewer.py +0 -0
  51. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/errors.py +0 -0
  52. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/errors.py +0 -0
  53. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/etloutput.py +0 -0
  54. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/download.py +0 -0
  55. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/indico_wrapper.py +0 -0
  56. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/modelop.py +0 -0
  57. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/workflow.py +0 -0
  58. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/metrics/__init__.py +0 -0
  59. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/metrics/compare_ground_truth.py +0 -0
  60. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/metrics/compare_models.py +0 -0
  61. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/metrics/metrics.py +0 -0
  62. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/metrics/plotting.py +0 -0
  63. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/ocr/ondoc_object.py +0 -0
  64. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/ocr/standard_object.py +0 -0
  65. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/pipelines/__init__.py +0 -0
  66. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/pipelines/file_processing.py +0 -0
  67. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/polling/__init__.py +0 -0
  68. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/py.typed +0 -0
  69. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/errors.py +0 -0
  70. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/extraction.py +0 -0
  71. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/prediction.py +0 -0
  72. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/retry.py +0 -0
  73. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/snapshots/__init__.py +0 -0
  74. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/snapshots/snapshot.py +0 -0
  75. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/structure/__init__.py +0 -0
  76. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/structure/create_structure.py +0 -0
  77. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/structure/queries.py +0 -0
  78. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/structure/utils.py +0 -0
  79. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/types/__init__.py +0 -0
  80. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/types/classification.py +0 -0
  81. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/types/extractions.py +0 -0
  82. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/indico_toolkit/types/predictions.py +0 -0
  83. {indico_toolkit-6.14.0 → indico_toolkit-6.14.2}/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.0
3
+ Version: 6.14.2
4
4
  Summary: Classes, functions, and abstractions for Indico IPA
5
5
  License: MIT
6
6
  Author: Indico Solutions Engineering
@@ -102,10 +102,10 @@ Formatting, linting, type checking, and tests are defined as
102
102
  [Poe](https://poethepoet.natn.io/) tasks in `pyproject.toml`.
103
103
 
104
104
  ```bash
105
- poetry run poe {format,check,test,all}
105
+ poetry poe {format,check,test,all}
106
106
  ```
107
107
 
108
- Code changes or additions should pass `poetry run poe all` before opening a PR.
108
+ Code changes or additions should pass `poetry poe all` before opening a PR.
109
109
 
110
110
 
111
111
  ### Tests
@@ -117,7 +117,7 @@ By default, only required unit tests are executed. Extra unit tests and integrat
117
117
  tests are skipped.
118
118
 
119
119
  ```bash
120
- poetry run poe {test,all}
120
+ poetry poe {test,all}
121
121
  ```
122
122
 
123
123
  Extra unit tests are skipped when their dependencies are not installed. To execute extra
@@ -125,7 +125,7 @@ unit tests, install one or more extras and run the tests.
125
125
 
126
126
  ```bash
127
127
  poetry install --all-extras
128
- poetry run poe {test,all}
128
+ poetry poe {test,all}
129
129
  ```
130
130
 
131
131
  Integration tests make API calls to an IPA environment and require a host and API token
@@ -133,7 +133,7 @@ to execute. These tests create datasets, setup workflows, and train models. **Ex
133
133
  them to take tens of minutes to run.**
134
134
 
135
135
  ```bash
136
- poetry run poe test-integration \
136
+ poetry poe test-integration \
137
137
  --host try.indico.io \
138
138
  --token indico_api_token.txt
139
139
  ```
@@ -68,10 +68,10 @@ Formatting, linting, type checking, and tests are defined as
68
68
  [Poe](https://poethepoet.natn.io/) tasks in `pyproject.toml`.
69
69
 
70
70
  ```bash
71
- poetry run poe {format,check,test,all}
71
+ poetry poe {format,check,test,all}
72
72
  ```
73
73
 
74
- Code changes or additions should pass `poetry run poe all` before opening a PR.
74
+ Code changes or additions should pass `poetry poe all` before opening a PR.
75
75
 
76
76
 
77
77
  ### Tests
@@ -83,7 +83,7 @@ By default, only required unit tests are executed. Extra unit tests and integrat
83
83
  tests are skipped.
84
84
 
85
85
  ```bash
86
- poetry run poe {test,all}
86
+ poetry poe {test,all}
87
87
  ```
88
88
 
89
89
  Extra unit tests are skipped when their dependencies are not installed. To execute extra
@@ -91,7 +91,7 @@ unit tests, install one or more extras and run the tests.
91
91
 
92
92
  ```bash
93
93
  poetry install --all-extras
94
- poetry run poe {test,all}
94
+ poetry poe {test,all}
95
95
  ```
96
96
 
97
97
  Integration tests make API calls to an IPA environment and require a host and API token
@@ -99,7 +99,7 @@ to execute. These tests create datasets, setup workflows, and train models. **Ex
99
99
  them to take tens of minutes to run.**
100
100
 
101
101
  ```bash
102
- poetry run poe test-integration \
102
+ poetry poe test-integration \
103
103
  --host try.indico.io \
104
104
  --token indico_api_token.txt
105
105
  ```
@@ -21,4 +21,4 @@ __all__ = (
21
21
  "ToolkitStaggeredLoopError",
22
22
  "ToolkitStatusError",
23
23
  )
24
- __version__ = "6.14.0"
24
+ __version__ = "6.14.2"
@@ -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,
@@ -1,7 +1,7 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
3
  from ..results import NULL_BOX, NULL_SPAN, Box, Span
4
- from ..results.utilities import get, has
4
+ from ..results.utils import get, has
5
5
  from .cell import Cell, CellType
6
6
  from .errors import EtlOutputError, TableCellNotFoundError, TokenNotFoundError
7
7
  from .etloutput import EtlOutput
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from enum import Enum
3
3
 
4
4
  from ..results import NULL_SPAN, Box, Span
5
- from ..results.utilities import get
5
+ from ..results.utils import get
6
6
  from .range import Range
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
 
3
- from ..results.utilities import get
3
+ from ..results.utils import get
4
4
 
5
5
 
6
6
  @dataclass(order=True, frozen=True)
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from operator import attrgetter
3
3
 
4
4
  from ..results import Box
5
- from ..results.utilities import get
5
+ from ..results.utils import get
6
6
  from .cell import Cell
7
7
 
8
8
 
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
 
3
3
  from ..results import Box, Span
4
- from ..results.utilities import get
4
+ from ..results.utils import get
5
5
 
6
6
 
7
7
  @dataclass(frozen=True)
@@ -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,
@@ -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(
@@ -23,7 +23,7 @@ from .predictions import (
23
23
  )
24
24
  from .result import Result
25
25
  from .review import Review, ReviewType
26
- from .utilities import get
26
+ from .utils import get
27
27
 
28
28
  if TYPE_CHECKING:
29
29
  from collections.abc import Awaitable, Callable
@@ -101,4 +101,4 @@ def _load(result: object) -> Result:
101
101
  elif file_version == 3:
102
102
  return Result.from_v3_dict(result)
103
103
  else:
104
- raise ResultError(f"unsupported file version `{file_version!r}`")
104
+ raise ResultError(f"unsupported file version `{file_version}`")
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
 
3
- from .utilities import get
3
+ from .utils import get
4
4
 
5
5
 
6
6
  @dataclass(frozen=True, order=True)
@@ -12,13 +12,14 @@ class Document:
12
12
  error: str
13
13
  traceback: str
14
14
 
15
- # Auto review changes must reproduce all model sections that were present in the
16
- # original result file. This may not be possible from the predictions alone--if a
17
- # model had an empty section because it didn't produce predictions or if all of
18
- # the predictions were removed to reject them. As such, the models seen when
19
- # parsing result files are tracked per-document so that the empty sections can be
20
- # reproduced later.
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 models and components seen when parsing a result file are tracked
20
+ # per-document so that the empty sections can be reproduced later.
21
21
  _model_sections: "frozenset[str]"
22
+ _component_sections: "frozenset[str]"
22
23
 
23
24
  @staticmethod
24
25
  def from_v1_dict(result: object) -> "Document":
@@ -38,6 +39,7 @@ class Document:
38
39
  error="",
39
40
  traceback="",
40
41
  _model_sections=model_names,
42
+ _component_sections=frozenset(),
41
43
  )
42
44
 
43
45
  @staticmethod
@@ -46,7 +48,9 @@ class Document:
46
48
  Create a `Document` from a v3 document dictionary.
47
49
  """
48
50
  model_results = get(document, dict, "model_results", "ORIGINAL")
51
+ component_results = get(document, dict, "component_results", "ORIGINAL")
49
52
  model_ids = frozenset(model_results.keys())
53
+ component_ids = frozenset(component_results.keys())
50
54
  etl_output_uri = get(document, str, "etl_output")
51
55
 
52
56
  return Document(
@@ -57,6 +61,7 @@ class Document:
57
61
  error="",
58
62
  traceback="",
59
63
  _model_sections=model_ids,
64
+ _component_sections=component_ids,
60
65
  )
61
66
 
62
67
  @staticmethod
@@ -75,4 +80,5 @@ class Document:
75
80
  error=error,
76
81
  traceback=traceback,
77
82
  _model_sections=frozenset(),
83
+ _component_sections=frozenset(),
78
84
  )
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from enum import Enum
3
3
 
4
- from .utilities import get, has
4
+ from .utils import get, has
5
5
 
6
6
 
7
7
  class ModelGroupType(Enum):
@@ -1,7 +1,8 @@
1
1
  import re
2
+ from itertools import chain
2
3
  from typing import TYPE_CHECKING
3
4
 
4
- from .utilities import get, has
5
+ from .utils import get, has
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from collections.abc import Iterator
@@ -88,12 +89,18 @@ def normalize_v3_result(result: "Any") -> None:
88
89
  """
89
90
  task_type_by_model_group_id = {
90
91
  model_group_id: model_group["task_type"]
91
- for model_group_id, model_group in result["modelgroup_metadata"].items()
92
+ for model_group_id, model_group in chain(
93
+ result["modelgroup_metadata"].items(),
94
+ result.get("component_metadata", {}).items(),
95
+ )
92
96
  }
93
97
  predictions_with_task_type: "Iterator[tuple[Any, str]]" = (
94
- (prediction, task_type_by_model_group_id[model_group_id])
98
+ (prediction, task_type_by_model_group_id.get(model_group_id, ""))
95
99
  for submission_result in get(result, list, "submission_results")
96
- for review_result in get(submission_result, dict, "model_results").values()
100
+ for review_result in chain(
101
+ get(submission_result, dict, "model_results").values(),
102
+ get(submission_result, dict, "component_results").values(),
103
+ )
97
104
  for model_group_id, model_results in review_result.items()
98
105
  for prediction in model_results
99
106
  )
@@ -139,6 +146,10 @@ def normalize_v3_result(result: "Any") -> None:
139
146
  if task_type == "summarization" and "citations" not in prediction:
140
147
  prediction["citations"] = []
141
148
 
149
+ # Prior to 7.2, v3 result files don't include a `component_metadata` section.
150
+ if not has(result, dict, "component_metadata"):
151
+ result["component_metadata"] = {}
152
+
142
153
  # Prior to 6.8, v3 result files don't include a `reviews` section.
143
154
  if not has(result, dict, "reviews"):
144
155
  result["reviews"] = {}
@@ -13,7 +13,7 @@ from .predictions import (
13
13
  Unbundling,
14
14
  )
15
15
  from .review import Review, ReviewType
16
- from .utilities import nfilter
16
+ from .utils import nfilter
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from collections.abc import Callable, Collection, Container, Iterable
@@ -337,7 +337,7 @@ class PredictionList(List[PredictionType]):
337
337
  elif result.version == 3:
338
338
  return self.to_v3_changes(result.documents)
339
339
  else:
340
- raise ValueError(f"unsupported file version `{result.version!r}`")
340
+ raise ValueError(f"unsupported file version `{result.version}`")
341
341
 
342
342
  def to_v1_changes(self, document: "Document") -> "dict[str, Any]":
343
343
  """
@@ -370,13 +370,8 @@ class PredictionList(List[PredictionType]):
370
370
  continue
371
371
 
372
372
  model_results: "dict[str, Any]" = {}
373
- changes.append(
374
- {
375
- "submissionfile_id": document.id,
376
- "model_results": model_results,
377
- "component_results": {},
378
- }
379
- )
373
+ component_results: "dict[str, Any]" = {}
374
+
380
375
  predictions_by_model = self.where(
381
376
  document=document,
382
377
  ).groupby(
@@ -384,12 +379,30 @@ class PredictionList(List[PredictionType]):
384
379
  )
385
380
 
386
381
  for model, predictions in predictions_by_model.items():
387
- model_results[str(model.id)] = [
382
+ model_id = str(model.id)
383
+ prediction_dicts = [
388
384
  prediction.to_v3_dict() for prediction in predictions
389
385
  ]
390
386
 
387
+ if model_id in document._model_sections:
388
+ model_results[model_id] = prediction_dicts
389
+ elif model_id in document._component_sections:
390
+ component_results[model_id] = prediction_dicts
391
+
391
392
  for model_id in document._model_sections:
392
393
  if model_id not in model_results:
393
394
  model_results[model_id] = []
394
395
 
396
+ for component_id in document._component_sections:
397
+ if component_id not in component_results:
398
+ component_results[component_id] = []
399
+
400
+ changes.append(
401
+ {
402
+ "submissionfile_id": document.id,
403
+ "model_results": model_results,
404
+ "component_results": component_results,
405
+ }
406
+ )
407
+
395
408
  return changes
@@ -62,7 +62,7 @@ def from_v1_dict(
62
62
  elif model.type == FORM_EXTRACTION:
63
63
  return FormExtraction.from_v1_dict(document, model, review, prediction)
64
64
  else:
65
- raise ResultError(f"unsupported v1 model type `{model.type!r}`")
65
+ raise ResultError(f"unsupported v1 model type {model.type!r}")
66
66
 
67
67
 
68
68
  def from_v3_dict(
@@ -85,4 +85,4 @@ def from_v3_dict(
85
85
  elif model.type == UNBUNDLING:
86
86
  return Unbundling.from_v3_dict(document, model, review, prediction)
87
87
  else:
88
- raise ResultError(f"unsupported v3 model type `{model.type!r}`")
88
+ raise ResultError(f"unsupported v3 model type {model.type!r}")
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- from ..utilities import get
4
+ from ..utils import get
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from typing import Final
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- from ..utilities import get
4
+ from ..utils import get
5
5
  from .span import NULL_SPAN, Span
6
6
 
7
7
  if TYPE_CHECKING:
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..review import Review
5
- from ..utilities import get, omit
5
+ from ..utils import get, omit
6
6
  from .prediction import Prediction
7
7
 
8
8
  if TYPE_CHECKING:
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..review import Review
5
- from ..utilities import get, has, omit
5
+ from ..utils import get, has, omit
6
6
  from .extraction import Extraction
7
7
  from .group import Group
8
8
  from .span import NULL_SPAN, Span
@@ -127,8 +127,10 @@ class DocumentExtraction(Extraction):
127
127
  "end": self.span.end,
128
128
  }
129
129
 
130
- prediction["normalized"]["formatted"] = self.text
131
- prediction["text"] = self.text # 6.10 sometimes reverts to raw text in review.
130
+ if self.text != get(prediction, str, "normalized", "formatted"):
131
+ prediction["normalized"]["formatted"] = self.text
132
+ prediction["normalized"]["text"] = self.text
133
+ prediction["text"] = self.text
132
134
 
133
135
  if self.accepted:
134
136
  prediction["accepted"] = True
@@ -149,8 +151,10 @@ class DocumentExtraction(Extraction):
149
151
  "spans": [span.to_dict() for span in self.spans],
150
152
  }
151
153
 
152
- prediction["normalized"]["formatted"] = self.text
153
- prediction["text"] = self.text # 6.10 sometimes reverts to raw text in review.
154
+ if self.text != get(prediction, str, "normalized", "formatted"):
155
+ prediction["normalized"]["formatted"] = self.text
156
+ prediction["normalized"]["text"] = self.text
157
+ prediction["text"] = self.text
154
158
 
155
159
  if self.accepted:
156
160
  prediction["accepted"] = True
@@ -3,7 +3,7 @@ from enum import Enum
3
3
  from typing import TYPE_CHECKING
4
4
 
5
5
  from ..review import Review
6
- from ..utilities import get, has, omit
6
+ from ..utils import get, has, omit
7
7
  from .box import Box
8
8
  from .extraction import Extraction
9
9
 
@@ -97,18 +97,23 @@ class FormExtraction(Extraction):
97
97
  }
98
98
 
99
99
  if self.type == FormExtractionType.CHECKBOX:
100
- prediction["normalized"]["structured"]["checked"] = self.checked
101
- prediction["normalized"]["formatted"] = (
102
- "Checked" if self.checked else "Unchecked"
103
- )
100
+ prediction["normalized"]["structured"] = {"checked": self.checked}
101
+ text = "Checked" if self.checked else "Unchecked"
102
+ prediction["normalized"]["formatted"] = text
103
+ prediction["normalized"]["text"] = self.text
104
+ prediction["text"] = self.text
104
105
  elif self.type == FormExtractionType.SIGNATURE:
105
- prediction["normalized"]["structured"]["signed"] = self.signed
106
- prediction["normalized"]["formatted"] = (
107
- "Signed" if self.signed else "Unsigned"
108
- )
106
+ prediction["normalized"]["structured"] = {"signed": self.signed}
107
+ text = "Signed" if self.signed else "Unsigned"
108
+ prediction["normalized"]["formatted"] = text
109
+ # Don't overwrite the text of the signature stored in these attributes.
110
+ # prediction["normalized"]["text"] = self.text
111
+ # prediction["text"] = self.text
109
112
  elif self.type == FormExtractionType.TEXT:
110
- prediction["normalized"]["formatted"] = self.text
111
- prediction["text"] = self.text # 6.10 sometimes reverts to text in review.
113
+ if self.text != get(prediction, str, "normalized", "formatted"):
114
+ prediction["normalized"]["formatted"] = self.text
115
+ prediction["normalized"]["text"] = self.text
116
+ prediction["text"] = self.text
112
117
 
113
118
  if self.accepted:
114
119
  prediction["accepted"] = True
@@ -1,7 +1,7 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, replace
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- from ..utilities import get
4
+ from ..utils import get
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from typing import Any
@@ -13,6 +13,14 @@ class Group:
13
13
  name: str
14
14
  index: int
15
15
 
16
+ def __next__(self) -> "Group":
17
+ """
18
+ Return the `Group` with the next index.
19
+
20
+ Supports `group = next(group)`.
21
+ """
22
+ return replace(self, index=self.index + 1)
23
+
16
24
  @staticmethod
17
25
  def from_dict(group: object) -> "Group":
18
26
  return Group(
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- from ..utilities import get
4
+ from ..utils import get
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from typing import Any, Final
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..review import Review
5
- from ..utilities import get, has, omit
5
+ from ..utils import get, has, omit
6
6
  from .citation import NULL_CITATION, Citation
7
7
  from .extraction import Extraction
8
8