indico_toolkit 6.14.1__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.1 → indico_toolkit-6.14.2}/PKG-INFO +1 -1
  2. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/__init__.py +1 -1
  3. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/client.py +1 -1
  4. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/__init__.py +0 -6
  5. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/ocr/__init__.py +0 -2
  6. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/polling/autoreview.py +3 -3
  7. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/polling/downstream.py +4 -4
  8. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/polling/queries.py +3 -3
  9. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/__init__.py +1 -1
  10. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/document.py +12 -6
  11. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/normalization.py +14 -3
  12. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictionlist.py +22 -9
  13. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/__init__.py +2 -2
  14. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/group.py +9 -1
  15. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/unbundling.py +11 -6
  16. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/result.py +58 -7
  17. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/utils.py +19 -17
  18. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/pyproject.toml +1 -1
  19. indico_toolkit-6.14.1/indico_toolkit/auto_populate/__init__.py +0 -3
  20. indico_toolkit-6.14.1/indico_toolkit/auto_populate/populator.py +0 -362
  21. indico_toolkit-6.14.1/indico_toolkit/auto_populate/types.py +0 -66
  22. indico_toolkit-6.14.1/indico_toolkit/indico_wrapper/dataset.py +0 -114
  23. indico_toolkit-6.14.1/indico_toolkit/indico_wrapper/doc_extraction.py +0 -79
  24. indico_toolkit-6.14.1/indico_toolkit/indico_wrapper/reviewer.py +0 -111
  25. indico_toolkit-6.14.1/indico_toolkit/ocr/customocr_object.py +0 -42
  26. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/LICENSE +0 -0
  27. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/README.md +0 -0
  28. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/association/__init__.py +0 -0
  29. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/association/association.py +0 -0
  30. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/association/extracted_tokens.py +0 -0
  31. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/association/line_items.py +0 -0
  32. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/association/positioning.py +0 -0
  33. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/association/splitting.py +0 -0
  34. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/auto_review/__init__.py +0 -0
  35. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/auto_review/auto_review_functions.py +0 -0
  36. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/auto_review/auto_reviewer.py +0 -0
  37. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/errors.py +0 -0
  38. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/__init__.py +0 -0
  39. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/cell.py +0 -0
  40. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/errors.py +0 -0
  41. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/etloutput.py +0 -0
  42. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/range.py +0 -0
  43. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/table.py +0 -0
  44. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/etloutput/token.py +0 -0
  45. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/download.py +0 -0
  46. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/indico_wrapper.py +0 -0
  47. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/modelop.py +0 -0
  48. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/indico_wrapper/workflow.py +0 -0
  49. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/metrics/__init__.py +0 -0
  50. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/metrics/compare_ground_truth.py +0 -0
  51. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/metrics/compare_models.py +0 -0
  52. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/metrics/metrics.py +0 -0
  53. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/metrics/plotting.py +0 -0
  54. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/ocr/ondoc_object.py +0 -0
  55. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/ocr/standard_object.py +0 -0
  56. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/pipelines/__init__.py +0 -0
  57. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/pipelines/file_processing.py +0 -0
  58. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/polling/__init__.py +0 -0
  59. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/py.typed +0 -0
  60. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/errors.py +0 -0
  61. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/model.py +0 -0
  62. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/box.py +0 -0
  63. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/citation.py +0 -0
  64. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/classification.py +0 -0
  65. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/documentextraction.py +0 -0
  66. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/extraction.py +0 -0
  67. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/formextraction.py +0 -0
  68. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/prediction.py +0 -0
  69. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/span.py +0 -0
  70. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/predictions/summarization.py +0 -0
  71. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/results/review.py +0 -0
  72. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/retry.py +0 -0
  73. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/snapshots/__init__.py +0 -0
  74. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/snapshots/snapshot.py +0 -0
  75. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/structure/__init__.py +0 -0
  76. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/structure/create_structure.py +0 -0
  77. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/structure/queries.py +0 -0
  78. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/structure/utils.py +0 -0
  79. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/types/__init__.py +0 -0
  80. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/types/classification.py +0 -0
  81. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/types/extractions.py +0 -0
  82. {indico_toolkit-6.14.1 → indico_toolkit-6.14.2}/indico_toolkit/types/predictions.py +0 -0
  83. {indico_toolkit-6.14.1 → 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.1
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
@@ -21,4 +21,4 @@ __all__ = (
21
21
  "ToolkitStaggeredLoopError",
22
22
  "ToolkitStatusError",
23
23
  )
24
- __version__ = "6.14.1"
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,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(
@@ -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}`")
@@ -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,4 +1,5 @@
1
1
  import re
2
+ from itertools import chain
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  from .utils import get, has
@@ -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"] = {}
@@ -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,4 +1,4 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, replace
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..utils import get
@@ -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(
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
4
4
  from ..review import Review
5
5
  from ..utils import get, omit
6
6
  from .prediction import Prediction
7
+ from .span import Span
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from typing import Any
@@ -14,7 +15,14 @@ if TYPE_CHECKING:
14
15
 
15
16
  @dataclass
16
17
  class Unbundling(Prediction):
17
- pages: "list[int]"
18
+ spans: "list[Span]"
19
+
20
+ @property
21
+ def pages(self) -> "tuple[int, ...]":
22
+ """
23
+ Return the pages covered by `self.spans`.
24
+ """
25
+ return tuple(span.page for span in self.spans)
18
26
 
19
27
  @staticmethod
20
28
  def from_v3_dict(
@@ -32,10 +40,7 @@ class Unbundling(Prediction):
32
40
  review=review,
33
41
  label=get(prediction, str, "label"),
34
42
  confidences=get(prediction, dict, "confidence"),
35
- pages=[
36
- get(span, int, "page_num")
37
- for span in get(prediction, list, "spans") # fmt: skip
38
- ],
43
+ spans=sorted(map(Span.from_dict, get(prediction, list, "spans"))),
39
44
  extras=omit(prediction, "confidence", "label", "spans"),
40
45
  )
41
46
 
@@ -47,5 +52,5 @@ class Unbundling(Prediction):
47
52
  **self.extras,
48
53
  "label": self.label,
49
54
  "confidence": self.confidences,
50
- "spans": [{"page_num": page} for page in self.pages],
55
+ "spans": [span.to_dict() for span in self.spans],
51
56
  }
@@ -5,12 +5,13 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  from . import predictions as prediction
7
7
  from .document import Document
8
+ from .errors import ResultError
8
9
  from .model import ModelGroup
9
10
  from .normalization import normalize_v1_result, normalize_v3_result
10
11
  from .predictionlist import PredictionList
11
12
  from .predictions import Prediction
12
13
  from .review import Review, ReviewType
13
- from .utils import get
14
+ from .utils import get, has
14
15
 
15
16
  if TYPE_CHECKING:
16
17
  from typing import Any
@@ -106,16 +107,33 @@ class Result:
106
107
  submission_id = get(result, int, "submission_id")
107
108
  submission_results = get(result, list, "submission_results")
108
109
  modelgroup_metadata = get(result, dict, "modelgroup_metadata")
110
+ component_metadata = get(result, dict, "component_metadata")
109
111
  review_metadata = get(result, dict, "reviews")
110
-
111
- processed_documents = map(Document.from_v3_dict, submission_results)
112
112
  errored_files = get(result, dict, "errored_files").values()
113
- failed_documents = map(Document.from_v3_errored_file, errored_files)
114
- documents = sorted(chain(processed_documents, failed_documents))
115
- models = sorted(map(ModelGroup.from_v3_dict, modelgroup_metadata.values()))
116
- predictions: "PredictionList[Prediction]" = PredictionList()
113
+
114
+ static_model_components = filter(
115
+ lambda component: (
116
+ get(component, str, "component_type").casefold() == "static_model"
117
+ ),
118
+ component_metadata.values(),
119
+ )
120
+
121
+ documents = sorted(
122
+ chain(
123
+ map(Document.from_v3_dict, submission_results),
124
+ map(Document.from_v3_errored_file, errored_files),
125
+ )
126
+ )
127
+ models = sorted(
128
+ chain(
129
+ map(ModelGroup.from_v3_dict, modelgroup_metadata.values()),
130
+ map(ModelGroup.from_v3_dict, static_model_components),
131
+ )
132
+ )
117
133
  reviews = sorted(map(Review.from_dict, review_metadata.values()))
118
134
 
135
+ predictions: "PredictionList[Prediction]" = PredictionList()
136
+
119
137
  for document_dict in submission_results:
120
138
  document_id = get(document_dict, int, "submissionfile_id")
121
139
  document = next(
@@ -124,11 +142,17 @@ class Result:
124
142
  reviewed_model_predictions: "list[tuple[Review | None, Any]]" = [
125
143
  (None, get(document_dict, dict, "model_results", "ORIGINAL"))
126
144
  ]
145
+ reviewed_component_predictions: "list[tuple[Review | None, Any]]" = [
146
+ (None, get(document_dict, dict, "component_results", "ORIGINAL"))
147
+ ]
127
148
 
128
149
  if reviews:
129
150
  reviewed_model_predictions.append(
130
151
  (reviews[-1], get(document_dict, dict, "model_results", "FINAL"))
131
152
  )
153
+ reviewed_component_predictions.append(
154
+ (reviews[-1], get(document_dict, dict, "component_results", "FINAL")) # fmt: skip # noqa: E501
155
+ )
132
156
 
133
157
  for review, model_section in reviewed_model_predictions:
134
158
  for model_id, model_predictions in model_section.items():
@@ -142,6 +166,33 @@ class Result:
142
166
  )
143
167
  )
144
168
 
169
+ for review, component_section in reviewed_component_predictions:
170
+ for component_id, component_predictions in component_section.items():
171
+ try:
172
+ model = next(
173
+ filter(lambda model: model.id == int(component_id), models)
174
+ )
175
+ except StopIteration:
176
+ if has(component_metadata, str, component_id, "component_type"):
177
+ component_type = get(
178
+ component_metadata, str, component_id, "component_type"
179
+ )
180
+ raise ResultError(
181
+ f"unsupported component type {component_type!r} "
182
+ f"for component {component_id}"
183
+ )
184
+ else:
185
+ raise ResultError(
186
+ f"no component metadata for component {component_id}"
187
+ )
188
+
189
+ predictions.extend(
190
+ map(
191
+ partial(prediction.from_v3_dict, document, model, review),
192
+ component_predictions,
193
+ )
194
+ )
195
+
145
196
  return Result(
146
197
  version=version,
147
198
  submission_id=submission_id,
@@ -1,33 +1,35 @@
1
1
  from collections.abc import Iterable, Iterator
2
2
  from typing import Callable, TypeVar
3
3
 
4
- from .errors import ResultError
5
-
6
4
  Value = TypeVar("Value")
7
5
 
8
6
 
9
7
  def get(result: object, value_type: "type[Value]", *keys: "str | int") -> Value:
10
8
  """
11
- Return the value obtained by traversing `result` using `keys` as indices if that
12
- value has type `value_type`. Raise a `ResultError` otherwise.
9
+ Return the value of type `value_type` obtained by traversing `result` using `keys`.
10
+ Raise an error if a key doesn't exist or the value has the wrong type.
13
11
  """
14
12
  for key in keys:
15
- if isinstance(key, str) and isinstance(result, dict) and key in result:
16
- result = result[key]
17
- elif isinstance(key, int) and isinstance(result, list) and key < len(result):
18
- result = result[key]
13
+ if isinstance(result, dict):
14
+ if key in result:
15
+ result = result[key]
16
+ else:
17
+ raise KeyError(f"{key!r} not in {result.keys()!r}")
18
+ elif isinstance(result, list):
19
+ if isinstance(key, int):
20
+ if 0 <= key < len(result):
21
+ result = result[key]
22
+ else:
23
+ raise IndexError(f"{key} out of range [0,{len(result)})")
24
+ else:
25
+ raise TypeError(f"list can't be indexed with {key!r}")
19
26
  else:
20
- raise ResultError(
21
- f"result object `{type(result)!r}` does not contain key `{key!r}`"
22
- )
27
+ raise TypeError(f"{type(result)} can't be traversed")
23
28
 
24
29
  if isinstance(result, value_type):
25
30
  return result
26
31
  else:
27
- raise ResultError(
28
- f"result object `{type(result)!r}` does not have a value for "
29
- f"key `{key!r}` of type `{value_type}`"
30
- )
32
+ raise TypeError(f"value `{result!r}` doesn't have type {value_type}")
31
33
 
32
34
 
33
35
  def has(result: object, value_type: "type[Value]", *keys: "str | int") -> bool:
@@ -35,9 +37,9 @@ def has(result: object, value_type: "type[Value]", *keys: "str | int") -> bool:
35
37
  Check if `result` can be traversed using `keys` to a value of type `value_type`.
36
38
  """
37
39
  for key in keys:
38
- if isinstance(key, str) and isinstance(result, dict) and key in result:
40
+ if isinstance(result, dict) and key in result:
39
41
  result = result[key]
40
- elif isinstance(key, int) and isinstance(result, list) and key < len(result):
42
+ elif isinstance(result, list) and isinstance(key, int) and 0 <= key < len(result): # fmt: skip # noqa: E501
41
43
  result = result[key]
42
44
  else:
43
45
  return False
@@ -12,7 +12,7 @@ authors = [
12
12
  readme = "README.md"
13
13
  urls = { source = "https://github.com/IndicoDataSolutions/Indico-Solutions-Toolkit" }
14
14
  requires-python = ">=3.10"
15
- version = "6.14.1"
15
+ version = "6.14.2"
16
16
  dependencies = ["indico-client (>=6.14.0,<7.0.0)"]
17
17
 
18
18
  [project.optional-dependencies]
@@ -1,3 +0,0 @@
1
- from .populator import AutoPopulator
2
-
3
- __all__ = ("AutoPopulator",)