destiny_sdk 0.4.1__tar.gz → 0.5.1__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 (35) hide show
  1. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/.gitignore +3 -2
  2. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/PKG-INFO +1 -1
  3. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/pyproject.toml +1 -1
  4. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/client.py +3 -2
  5. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/enhancements.py +27 -37
  6. destiny_sdk-0.5.1/src/destiny_sdk/identifiers.py +192 -0
  7. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/imports.py +23 -73
  8. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/parsers/eppi_parser.py +0 -1
  9. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/robots.py +11 -19
  10. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/visibility.py +3 -8
  11. destiny_sdk-0.5.1/tests/unit/test_data/eppi_import.jsonl +4 -0
  12. destiny_sdk-0.5.1/tests/unit/test_data/eppi_import_with_annotations.jsonl +4 -0
  13. destiny_sdk-0.5.1/tests/unit/test_identifiers.py +320 -0
  14. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_robots.py +1 -3
  15. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/uv.lock +1 -1
  16. destiny_sdk-0.4.1/src/destiny_sdk/identifiers.py +0 -110
  17. destiny_sdk-0.4.1/tests/unit/test_data/eppi_import.jsonl +0 -4
  18. destiny_sdk-0.4.1/tests/unit/test_data/eppi_import_with_annotations.jsonl +0 -4
  19. destiny_sdk-0.4.1/tests/unit/test_identifiers.py +0 -104
  20. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/LICENSE +0 -0
  21. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/README.md +0 -0
  22. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/__init__.py +0 -0
  23. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/auth.py +0 -0
  24. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/core.py +0 -0
  25. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/parsers/__init__.py +0 -0
  26. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/py.typed +0 -0
  27. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/references.py +0 -0
  28. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/__init__.py +0 -0
  29. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/conftest.py +0 -0
  30. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/parsers/test_eppi_parser.py +0 -0
  31. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_auth.py +0 -0
  32. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_client.py +0 -0
  33. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_data/eppi_report.json +0 -0
  34. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_enhancements.py +0 -0
  35. {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_references.py +0 -0
@@ -15,6 +15,7 @@ downloads/
15
15
  eggs/
16
16
  .eggs/
17
17
  lib/
18
+ !/ui/lib/
18
19
  lib64/
19
20
  parts/
20
21
  sdist/
@@ -186,8 +187,8 @@ cython_debug/
186
187
  # Mac Finder config
187
188
  .DS_Store
188
189
 
189
- # MinIO presigned URLs
190
- .minio/presigned_urls.json
190
+ # Include everything in ui (has its own .gitignore)
191
+ !/ui/
191
192
 
192
193
  # delete-me working files
193
194
  tmp-scripts.sh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: destiny_sdk
3
- Version: 0.4.1
3
+ Version: 0.5.1
4
4
  Summary: A software development kit (sdk) to support interaction with the DESTINY repository
5
5
  Author-email: Adam Hamilton <adam@futureevidence.org>, Andrew Harvey <andrew@futureevidence.org>, Daniel Breves <daniel@futureevidence.org>, Jack Walmisley <jack@futureevidence.org>
6
6
  License-Expression: Apache-2.0
@@ -33,7 +33,7 @@ license = "Apache-2.0"
33
33
  name = "destiny_sdk"
34
34
  readme = "README.md"
35
35
  requires-python = "~=3.12"
36
- version = "0.4.1"
36
+ version = "0.5.1"
37
37
 
38
38
  [tool.pytest.ini_options]
39
39
  addopts = ["--color=yes", "--import-mode=importlib", "--verbose"]
@@ -114,7 +114,7 @@ class Client:
114
114
  return RobotEnhancementBatchRead.model_validate(response.json())
115
115
 
116
116
  def poll_robot_enhancement_batch(
117
- self, robot_id: UUID4, limit: int = 10
117
+ self, robot_id: UUID4, limit: int = 10, timeout: int = 60
118
118
  ) -> RobotEnhancementBatch | None:
119
119
  """
120
120
  Poll for a robot enhancement batch.
@@ -127,11 +127,12 @@ class Client:
127
127
  :type limit: int
128
128
  :return: The RobotEnhancementBatch object from the response, or None if no
129
129
  batches available
130
- :rtype: RobotEnhancementBatch | None
130
+ :rtype: destiny_sdk.robots.RobotEnhancementBatch | None
131
131
  """
132
132
  response = self.session.post(
133
133
  "/robot-enhancement-batches/",
134
134
  params={"robot_id": str(robot_id), "limit": limit},
135
+ timeout=timeout,
135
136
  )
136
137
  # HTTP 204 No Content indicates no batches available
137
138
  if response.status_code == httpx.codes.NO_CONTENT:
@@ -15,20 +15,18 @@ class EnhancementType(StrEnum):
15
15
  The type of enhancement.
16
16
 
17
17
  This is used to identify the type of enhancement in the `Enhancement` class.
18
-
19
- **Allowed values**:
20
- - `bibliographic`: Bibliographic metadata.
21
- - `abstract`: The abstract of a reference.
22
- - `annotation`: A free-form enhancement for tagging with labels.
23
- - `locations`: Locations where the reference can be found.
24
- - `full_text`: The full text of the reference. (To be implemeted)
25
18
  """
26
19
 
27
20
  BIBLIOGRAPHIC = auto()
21
+ """Bibliographic metadata."""
28
22
  ABSTRACT = auto()
23
+ """The abstract of a reference."""
29
24
  ANNOTATION = auto()
25
+ """A free-form enhancement for tagging with labels."""
30
26
  LOCATION = auto()
27
+ """Locations where the reference can be found."""
31
28
  FULL_TEXT = auto()
29
+ """The full text of the reference. (To be implemented)"""
32
30
 
33
31
 
34
32
  class AuthorPosition(StrEnum):
@@ -36,16 +34,14 @@ class AuthorPosition(StrEnum):
36
34
  The position of an author in a list of authorships.
37
35
 
38
36
  Maps to the data from OpenAlex.
39
-
40
- **Allowed values**:
41
- - `first`: The first author.
42
- - `middle`: Any middle author
43
- - `last`: The last author
44
37
  """
45
38
 
46
39
  FIRST = auto()
40
+ """The first author."""
47
41
  MIDDLE = auto()
42
+ """Any middle author."""
48
43
  LAST = auto()
44
+ """The last author."""
49
45
 
50
46
 
51
47
  class Authorship(BaseModel):
@@ -104,18 +100,14 @@ other works have cited this work
104
100
 
105
101
 
106
102
  class AbstractProcessType(StrEnum):
107
- """
108
- The process used to acquire the abstract.
109
-
110
- **Allowed values**:
111
- - `uninverted`
112
- - `closed_api`
113
- - `other`
114
- """
103
+ """The process used to acquire the abstract."""
115
104
 
116
105
  UNINVERTED = auto()
106
+ """uninverted"""
117
107
  CLOSED_API = auto()
108
+ """closed_api"""
118
109
  OTHER = auto()
110
+ """other"""
119
111
 
120
112
 
121
113
  class AbstractContentEnhancement(BaseModel):
@@ -142,16 +134,15 @@ class AnnotationType(StrEnum):
142
134
  The type of annotation.
143
135
 
144
136
  This is used to identify the type of annotation in the `Annotation` class.
145
-
146
- **Allowed values**:
147
- - `boolean`: An annotation which is the boolean application of a label across a
148
- reference.
149
- - `score`: An annotation which is a score for a label across a reference,
150
- without a boolean value.
151
137
  """
152
138
 
153
139
  BOOLEAN = auto()
140
+ """An annotation which is the boolean application of a label across a reference."""
154
141
  SCORE = auto()
142
+ """
143
+ An annotation which is a score for a label across a reference, without a boolean
144
+ value.
145
+ """
155
146
 
156
147
 
157
148
  class ScoreAnnotation(BaseModel):
@@ -227,22 +218,22 @@ class DriverVersion(StrEnum):
227
218
  The version based on the DRIVER guidelines versioning scheme.
228
219
 
229
220
  (Borrowed from OpenAlex)
230
-
231
- Allowed values:
232
- - `publishedVersion`: The document's version of record. This is the most
233
- authoritative version.
234
- - `acceptedVersion`: The document after having completed peer review and being
235
- officially accepted for publication. It will lack publisher formatting, but the
236
- content should be interchangeable with the that of the publishedVersion.
237
- - `submittedVersion`: the document as submitted to the publisher by the authors, but
238
- before peer-review. Its content may differ significantly from that of the accepted
239
- article.
240
221
  """
241
222
 
242
223
  PUBLISHED_VERSION = "publishedVersion"
224
+ """The document's version of record. This is the most authoritative version."""
243
225
  ACCEPTED_VERSION = "acceptedVersion"
226
+ """
227
+ The document after having completed peer review and being officially accepted for
228
+ publication. It will lack publisher formatting, but the content should be
229
+ interchangeable with that of the publishedVersion.
230
+ """
244
231
  SUBMITTED_VERSION = "submittedVersion"
232
+ """
233
+ The document as submitted to the publisher by the authors, but before peer-review.
234
+ Its content may differ significantly from that of the accepted article."""
245
235
  OTHER = "other"
236
+ """Other version."""
246
237
 
247
238
 
248
239
  class Location(BaseModel):
@@ -360,7 +351,6 @@ class EnhancementFileInput(BaseModel):
360
351
  visibility: Visibility = Field(
361
352
  description="The level of visibility of the enhancement"
362
353
  )
363
- enhancement_type: EnhancementType = Field(description="The type of enhancement.")
364
354
  robot_version: str | None = Field(
365
355
  default=None,
366
356
  description="The version of the robot that generated the content.",
@@ -0,0 +1,192 @@
1
+ """Identifier classes for the Destiny SDK."""
2
+
3
+ import uuid
4
+ from enum import StrEnum, auto
5
+ from typing import Annotated, Literal, Self
6
+
7
+ from pydantic import UUID4, BaseModel, Field, TypeAdapter, field_validator
8
+
9
+
10
+ class ExternalIdentifierType(StrEnum):
11
+ """
12
+ The type of identifier used to identify a reference.
13
+
14
+ This is used to identify the type of identifier used in the `ExternalIdentifier`
15
+ class.
16
+ """
17
+
18
+ DOI = auto()
19
+ """A DOI (Digital Object Identifier) which is a unique identifier for a document."""
20
+ PM_ID = auto()
21
+ """A PubMed ID which is a unique identifier for a document in PubMed."""
22
+ OPEN_ALEX = auto()
23
+ """An OpenAlex ID which is a unique identifier for a document in OpenAlex."""
24
+ OTHER = auto()
25
+ """Any other identifier not defined. This should be used sparingly."""
26
+
27
+
28
+ class DOIIdentifier(BaseModel):
29
+ """An external identifier representing a DOI."""
30
+
31
+ identifier: str = Field(
32
+ description="The DOI of the reference.",
33
+ pattern=r"^10\.\d{4,9}/[-._;()/:a-zA-Z0-9%<>\[\]+&]+$",
34
+ )
35
+ identifier_type: Literal[ExternalIdentifierType.DOI] = Field(
36
+ ExternalIdentifierType.DOI, description="The type of identifier used."
37
+ )
38
+
39
+ @field_validator("identifier", mode="before")
40
+ @classmethod
41
+ def remove_doi_url(cls, value: str) -> str:
42
+ """Remove the URL part of the DOI if it exists."""
43
+ return (
44
+ value.removeprefix("http://doi.org/")
45
+ .removeprefix("https://doi.org/")
46
+ .strip()
47
+ )
48
+
49
+
50
+ class PubMedIdentifier(BaseModel):
51
+ """An external identifier representing a PubMed ID."""
52
+
53
+ identifier: int = Field(description="The PubMed ID of the reference.")
54
+ identifier_type: Literal[ExternalIdentifierType.PM_ID] = Field(
55
+ ExternalIdentifierType.PM_ID, description="The type of identifier used."
56
+ )
57
+
58
+
59
+ class OpenAlexIdentifier(BaseModel):
60
+ """An external identifier representing an OpenAlex ID."""
61
+
62
+ identifier: str = Field(
63
+ description="The OpenAlex ID of the reference.", pattern=r"^W\d+$"
64
+ )
65
+ identifier_type: Literal[ExternalIdentifierType.OPEN_ALEX] = Field(
66
+ ExternalIdentifierType.OPEN_ALEX, description="The type of identifier used."
67
+ )
68
+
69
+ @field_validator("identifier", mode="before")
70
+ @classmethod
71
+ def remove_open_alex_url(cls, value: str) -> str:
72
+ """Remove the OpenAlex URL if it exists."""
73
+ return (
74
+ value.removeprefix("http://openalex.org/")
75
+ .removeprefix("https://openalex.org/")
76
+ .strip()
77
+ )
78
+
79
+
80
+ class OtherIdentifier(BaseModel):
81
+ """An external identifier not otherwise defined by the repository."""
82
+
83
+ identifier: str = Field(description="The identifier of the reference.")
84
+ identifier_type: Literal[ExternalIdentifierType.OTHER] = Field(
85
+ ExternalIdentifierType.OTHER, description="The type of identifier used."
86
+ )
87
+ other_identifier_name: str = Field(
88
+ description="The name of the undocumented identifier type."
89
+ )
90
+
91
+
92
+ #: Union type for all external identifiers.
93
+ ExternalIdentifier = Annotated[
94
+ DOIIdentifier | PubMedIdentifier | OpenAlexIdentifier | OtherIdentifier,
95
+ Field(discriminator="identifier_type"),
96
+ ]
97
+
98
+ ExternalIdentifierAdapter: TypeAdapter[ExternalIdentifier] = TypeAdapter(
99
+ ExternalIdentifier
100
+ )
101
+
102
+
103
+ class LinkedExternalIdentifier(BaseModel):
104
+ """An external identifier which identifies a reference."""
105
+
106
+ identifier: ExternalIdentifier = Field(
107
+ description="The identifier of the reference.",
108
+ discriminator="identifier_type",
109
+ )
110
+ reference_id: UUID4 = Field(
111
+ description="The ID of the reference this identifier identifies."
112
+ )
113
+
114
+
115
+ class IdentifierLookup(BaseModel):
116
+ """An external identifier lookup."""
117
+
118
+ identifier: str = Field(description="The identifier value.")
119
+ identifier_type: ExternalIdentifierType | None = Field(
120
+ description="The type of identifier used. If not provided, it is assumed to"
121
+ " be a DESTINY identifier.",
122
+ )
123
+ other_identifier_name: str | None = Field(
124
+ default=None,
125
+ description="The name of the undocumented identifier type.",
126
+ )
127
+
128
+ def serialize(self) -> str:
129
+ """Serialize the identifier lookup to a string."""
130
+ if self.identifier_type is None:
131
+ return self.identifier
132
+ if self.identifier_type == ExternalIdentifierType.OTHER:
133
+ return f"other:{self.other_identifier_name}:{self.identifier}"
134
+ return f"{self.identifier_type.value.lower()}:{self.identifier}"
135
+
136
+ @classmethod
137
+ def parse(cls, identifier_lookup_string: str, delimiter: str = ":") -> Self:
138
+ """Parse an identifier string into an IdentifierLookup."""
139
+ if delimiter not in identifier_lookup_string:
140
+ try:
141
+ UUID4(identifier_lookup_string)
142
+ except ValueError as exc:
143
+ msg = (
144
+ f"Invalid identifier lookup string: {identifier_lookup_string}. "
145
+ "Must be UUIDv4 if no identifier type is specified."
146
+ )
147
+ raise ValueError(msg) from exc
148
+ return cls(
149
+ identifier=identifier_lookup_string,
150
+ identifier_type=None,
151
+ )
152
+ identifier_type, identifier = identifier_lookup_string.split(delimiter, 1)
153
+ if identifier_type == ExternalIdentifierType.OTHER:
154
+ if delimiter not in identifier:
155
+ msg = (
156
+ f"Invalid identifier lookup string: {identifier_lookup_string}. "
157
+ "Other identifier type must include other identifier name."
158
+ )
159
+ raise ValueError(msg)
160
+ other_identifier_type, identifier = identifier.split(delimiter, 1)
161
+ return cls(
162
+ identifier=identifier,
163
+ identifier_type=ExternalIdentifierType.OTHER,
164
+ other_identifier_name=other_identifier_type,
165
+ )
166
+ if identifier_type not in ExternalIdentifierType:
167
+ msg = (
168
+ f"Invalid identifier lookup string: {identifier_lookup_string}. "
169
+ f"Unknown identifier type: {identifier_type}."
170
+ )
171
+ raise ValueError(msg)
172
+ return cls(
173
+ identifier=identifier,
174
+ identifier_type=ExternalIdentifierType(identifier_type),
175
+ )
176
+
177
+ @classmethod
178
+ def from_identifier(cls, identifier: ExternalIdentifier | UUID4) -> Self:
179
+ """Create an IdentifierLookup from an ExternalIdentifier or UUID4."""
180
+ if isinstance(identifier, uuid.UUID):
181
+ return cls(identifier=str(identifier), identifier_type=None)
182
+ return cls(
183
+ identifier=str(identifier.identifier),
184
+ identifier_type=identifier.identifier_type,
185
+ other_identifier_name=getattr(identifier, "other_identifier_name", None),
186
+ )
187
+
188
+ def to_identifier(self) -> ExternalIdentifier | UUID4:
189
+ """Convert into an ExternalIdentifier or UUID4 if it has no identifier_type."""
190
+ if self.identifier_type is None:
191
+ return UUID4(self.identifier)
192
+ return ExternalIdentifierAdapter.validate_python(self.model_dump())
@@ -13,90 +13,52 @@ from pydantic import (
13
13
 
14
14
 
15
15
  class ImportRecordStatus(StrEnum):
16
- """
17
- Describes the status of an import record.
18
-
19
- - `created`: Created, but no processing has started.
20
- - `started`: Processing has started on the batch.
21
- - `completed`: Processing has been completed.
22
- """
16
+ """Describes the status of an import record."""
23
17
 
24
18
  CREATED = auto()
19
+ """Created, but no processing has started."""
25
20
  STARTED = auto()
21
+ """Processing has started on the batch."""
26
22
  COMPLETED = auto()
23
+ """Processing has been completed."""
27
24
 
28
25
 
29
26
  class ImportBatchStatus(StrEnum):
30
- """
31
- Describes the status of an import batch.
32
-
33
- - `created`: Created, but no processing has started.
34
- - `started`: Processing has started on the batch.
35
- - `failed`: Processing has failed.
36
- - `partially_failed`: Some references succeeded while others failed.
37
- - `completed`: Processing has been completed.
38
- """
27
+ """Describes the status of an import batch."""
39
28
 
40
29
  CREATED = auto()
30
+ """Created, but no processing has started."""
41
31
  STARTED = auto()
32
+ """Processing has started on the batch."""
42
33
  FAILED = auto()
34
+ """Processing has failed."""
43
35
  PARTIALLY_FAILED = auto()
36
+ """Some references succeeded while others failed."""
44
37
  COMPLETED = auto()
45
-
46
-
47
- class CollisionStrategy(StrEnum):
48
- """
49
- The strategy to use when an identifier collision is detected.
50
-
51
- Identifier collisions are detected on ``identifier_type`` and ``identifier``
52
- (and ``other_identifier_name`` where relevant) already present in the database.
53
-
54
- Enhancement collisions are detected on an entry with matching ``enhancement_type``
55
- and ``source`` already being present on the collided reference.
56
-
57
- - `discard`: Do nothing with the incoming reference.
58
- - `fail`: Do nothing with the incoming reference and mark it as failed. This
59
- allows the importing process to "follow up" on the failure.
60
- - `merge_aggressive`: Prioritize the incoming reference's identifiers and
61
- enhancements in the merge.
62
- - `merge_defensive`: Prioritize the existing reference's identifiers and
63
- enhancements in the merge.
64
- - `append`: Performs an aggressive merge of identifiers, and an append of
65
- enhancements.
66
- - `overwrite`: Performs an aggressive merge of identifiers, and an overwrite of
67
- enhancements (deleting existing and recreating what is imported). This should
68
- be used sparingly and carefully.
69
- """
70
-
71
- DISCARD = auto()
72
- FAIL = auto()
73
- MERGE_AGGRESSIVE = auto()
74
- MERGE_DEFENSIVE = auto()
75
- APPEND = auto()
76
- OVERWRITE = auto()
38
+ """Processing has been completed."""
77
39
 
78
40
 
79
41
  class ImportResultStatus(StrEnum):
80
- """
81
- Describes the status of an import result.
82
-
83
- - `created`: Created, but no processing has started.
84
- - `started`: The reference is currently being processed.
85
- - `completed`: The reference has been created.
86
- - `partially_failed`: The reference was created but one or more enhancements or
87
- identifiers failed to be added. See the result's `failure_details` field for
88
- more information.
89
- - `failed`: The reference failed to be created. See the result's `failure_details`
90
- field for more information.
91
- - `retrying`: Processing has failed, but is being retried.
92
- """
42
+ """Describes the status of an import result."""
93
43
 
94
44
  CREATED = auto()
45
+ """Created, but no processing has started."""
95
46
  STARTED = auto()
47
+ """The reference is currently being processed."""
96
48
  COMPLETED = auto()
49
+ """The reference has been created."""
97
50
  PARTIALLY_FAILED = auto()
51
+ """
52
+ The reference was created but one or more enhancements or identifiers failed to
53
+ be added. See the result's `failure_details` field for more information.
54
+ """
98
55
  FAILED = auto()
56
+ """
57
+ The reference failed to be created. See the result's `failure_details` field for
58
+ more information.
59
+ """
99
60
  RETRYING = auto()
61
+ """Processing has failed, but is being retried."""
100
62
 
101
63
 
102
64
  class _ImportRecordBase(BaseModel):
@@ -162,13 +124,6 @@ class ImportRecordRead(_ImportRecordBase):
162
124
  class _ImportBatchBase(BaseModel):
163
125
  """The base class for import batches."""
164
126
 
165
- collision_strategy: CollisionStrategy = Field(
166
- default=CollisionStrategy.FAIL,
167
- description="""
168
- The strategy to use for each reference when an identifier collision occurs.
169
- Default is `fail`, which allows the importing process to "follow up" on the collision.
170
- """,
171
- )
172
127
  storage_url: HttpUrl = Field(
173
128
  description="""
174
129
  The URL at which the set of references for this batch are stored. The file is a jsonl
@@ -176,11 +131,6 @@ with each line formatted according to
176
131
  :class:`ReferenceFileInput <libs.sdk.src.destiny_sdk.references.ReferenceFileInput>`.
177
132
  """,
178
133
  )
179
- callback_url: HttpUrl | None = Field(
180
- default=None,
181
- deprecated=True,
182
- description="This field is currently a no-op.",
183
- )
184
134
 
185
135
 
186
136
  class ImportBatchIn(_ImportBatchBase):
@@ -157,7 +157,6 @@ class EPPIParser:
157
157
  source=parser_source,
158
158
  visibility=Visibility.PUBLIC,
159
159
  content=content,
160
- enhancement_type=content.enhancement_type,
161
160
  robot_version=robot_version,
162
161
  )
163
162
  for content in enhancement_contents
@@ -159,32 +159,28 @@ If the URL expires, a new one can be generated using
159
159
 
160
160
 
161
161
  class EnhancementRequestStatus(StrEnum):
162
- """
163
- The status of an enhancement request.
164
-
165
- **Allowed values**:
166
- - `received`: Enhancement request has been received by the repo.
167
- - `accepted`: Enhancement request has been accepted by the robot.
168
- - `processing`: Enhancement request is being processed by the robot.
169
- - `rejected`: Enhancement request has been rejected by the robot.
170
- - `partial_failed`: Some enhancements failed to create.
171
- - `failed`: All enhancements failed to create.
172
- - `importing`: Enhancements have been received by the repo and are being imported.
173
- - `indexing`: Enhancements have been imported and are being indexed.
174
- - `indexing_failed`: Enhancements have been imported but indexing failed.
175
- - `completed`: All enhancements have been created.
176
- """
162
+ """The status of an enhancement request."""
177
163
 
178
164
  RECEIVED = auto()
165
+ """Enhancement request has been received by the repo."""
179
166
  ACCEPTED = auto()
167
+ """Enhancement request has been accepted by the robot."""
180
168
  PROCESSING = auto()
169
+ """Enhancement request is being processed by the robot."""
181
170
  REJECTED = auto()
171
+ """Enhancement request has been rejected by the robot."""
182
172
  PARTIAL_FAILED = auto()
173
+ """Some enhancements failed to create."""
183
174
  FAILED = auto()
175
+ """All enhancements failed to create."""
184
176
  IMPORTING = auto()
177
+ """Enhancements have been received by the repo and are being imported."""
185
178
  INDEXING = auto()
179
+ """Enhancements have been imported and are being indexed."""
186
180
  INDEXING_FAILED = auto()
181
+ """Enhancements have been imported but indexing failed."""
187
182
  COMPLETED = auto()
183
+ """All enhancements have been created."""
188
184
 
189
185
 
190
186
  class _EnhancementRequestBase(BaseModel):
@@ -334,10 +330,6 @@ class _RobotBase(BaseModel):
334
330
  model_config = ConfigDict(extra="forbid") # Forbid extra fields on robot models
335
331
 
336
332
  name: str = Field(description="The name of the robot, must be unique.")
337
- base_url: HttpUrl = Field(
338
- description="The base url of the robot. The robot must implement endpoint"
339
- "base_url/batch for batch enhancements of references.",
340
- )
341
333
  description: str = Field(
342
334
  description="Description of the enhancement the robot provides."
343
335
  )
@@ -9,16 +9,11 @@ class Visibility(StrEnum):
9
9
 
10
10
  This is used to manage whether information should be publicly available or
11
11
  restricted (generally due to copyright constraints from publishers).
12
-
13
- TODO: Implement data governance layer to manage this.
14
-
15
- **Allowed values**:
16
-
17
- - `public`: Visible to the general public without authentication.
18
- - `restricted`: Requires authentication to be visible.
19
- - `hidden`: Is not visible, but may be passed to data mining processes.
20
12
  """
21
13
 
22
14
  PUBLIC = auto()
15
+ """Visible to the general public without authentication."""
23
16
  RESTRICTED = auto()
17
+ """Requires authentication to be visible."""
24
18
  HIDDEN = auto()
19
+ """Is not visible, but may be passed to data mining processes."""
@@ -0,0 +1,4 @@
1
+ {"visibility":"public","identifiers":[{"identifier":"10.1016/j.eclinm.2024.102524","identifier_type":"doi"}],"enhancements":[{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"abstract","process":"other","abstract":"Background\nWhile human papillomavirus (HPV) vaccines have been available since 2006, the coverage has varied among countries. Our aim is to analyse the equity impact of HPV vaccination on the lifetime projections of cervical cancer burden among vaccinated cohorts of 2010–22 in 84 countries.\n\nMethods\nWe used WHO and UNICEF estimates of national immunisation coverage for HPV vaccination in 84 countries during 2010–22. We used PRIME (Papillomavirus Rapid Interface for Modelling and Economics) to estimate the lifetime health impact of HPV vaccination on cervical cancer burden in terms of deaths, cases, and disability-adjusted life years (DALYs) averted by vaccination in their respective countries. We generated concentration indices and curves to assess the equity impact of HPV vaccination across 84 countries.\n\nFindings\nThe health impact of HPV vaccination varied across the 84 countries and ranged from Switzerland to Tanzania at 2 to 34 deaths, 4 to 47 cases, and 40 to 735 DALYs averted per 1000 vaccinated adolescent girls over the lifetime of the vaccinated cohorts of 2010–22. The concentration index for the distribution of average coverage during 2010–22 among the 84 countries ranked by vaccine impact was 0.33 (95% CI: 0.27–0.40) and highlights the wide inequities in HPV vaccination coverage.\n\nInterpretation\nOur findings suggested that countries with a relatively higher cervical cancer burden and thereby a relatively higher need for HPV vaccination had relatively lower coverage during 2010–22. Further, there were significant inequities in HPV vaccination coverage within the Americas, Europe, and Western Pacific regions, and in high- and low-income countries with a pro-advantaged and regressive distribution favouring countries with lower vaccine impact."}},{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"bibliographic","authorship":[{"display_name":"Abbas Kaja","position":"first"},{"display_name":"Yoo Katelyn","position":"middle"},{"display_name":"Prem Kiesha","position":"middle"},{"display_name":"Jit Mark","position":"last"}],"publication_year":2024,"publisher":"Elsevier BV","title":"Equity impact of HPV vaccination on lifetime projections of cervical cancer burden among cohorts in 84 countries by global, regional, and income levels, 2010–22: a modelling study"}}]}
2
+ {"visibility":"public","identifiers":[{"identifier":"10.1016/j.jvacx.2024.100459","identifier_type":"doi"}],"enhancements":[{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"abstract","process":"other","abstract":"The World Health Organization has recommended the inclusion of human papillomavirus (HPV) vaccines in national immunization programs to address the global problem of cervical cancer. In the Philippines, HPV vaccination was introduced in a phased approach in 2015. This study seeks to estimate the cost of delivery of the HPV vaccination program and its operational context in the Philippines.\n\nMethods\nThis was a retrospective, cross-sectional micro-costing study focused on ongoing HPV vaccination delivery and its operational context across all levels of the health system. Using structured questionnaires and data collection from secondary sources, the weighted mean financial and economic costs and costs per dose at the national, subnational, and health facility levels were estimated.\nResults\nThe weighted mean financial and economic costs per dose of the HPV vaccination program aggregated across all levels of the health system were $US3.72and $29.74, respectively. Activities contributing most significantly to costs were service delivery and vaccine collection or distribution and storage at the health facility and administrative levels, respectively. The opportunity costs for health worker and non-health worker time accounted for 77% of the economic cost per dose.\nConclusion\nThe total weighted mean financial and economic costs of HPV delivery are within range of those reported in other countries. Costing studies can help identify cost drivers with local operational context to help inform policymakers and program managers in budgeting and planning interventions to improve program implementation."}},{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"bibliographic","authorship":[{"display_name":"Aldaba Josephine","position":"first"},{"display_name":"Llave Cecilia","position":"middle"},{"display_name":"Uy Ma","position":"middle"},{"display_name":"Tejano Kim","position":"middle"},{"display_name":"Aquino Ma","position":"middle"},{"display_name":"Catalig Migel","position":"middle"},{"display_name":"Sy Alvin","position":"middle"},{"display_name":"Valverde Haidee","position":"middle"},{"display_name":"Mooney Jessica","position":"middle"},{"display_name":"Slavkovsky Rose","position":"last"}],"publication_year":2024,"publisher":"Elsevier BV","title":"The cost of human papillomavirus vaccination delivery at the administrative and health facility levels in the Philippines"}}]}
3
+ {"visibility":"public","identifiers":[{"identifier":"10.1016/j.vaccine.2019.12.013","identifier_type":"doi"}],"enhancements":[{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"abstract","process":"other","abstract":"Human papillomavirus (HPV) vaccination has not been introduced in many countries in South-Central Asia, including Afghanistan, despite the sub-region having the highest incidence rate of cervical cancer in Asia. This study estimates the potential health impact and cost-effectiveness of HPV vaccination in Afghanistan to inform national decision-making. An Excel-based static cohort model was used to estimate the lifetime costs and health outcomes of vaccinating a single cohort of 9-year-old girls in the year 2018 with the bivalent HPV vaccine, compared to no vaccination. We also explored a scenario with a catch-up campaign for girls aged 10–14 years. Input parameters were based on local sources, published literature, or assumptions when no data was available. The primary outcome measure was the discounted cost per disability-adjusted life-year (DALY) averted, evaluated from both government and societal perspectives. Vaccinating a single cohort of 9-year-old girls against HPV in Afghanistan could avert 1718 cervical cancer cases, 125 hospitalizations, and 1612 deaths over the lifetime of the cohort. The incremental cost-effectiveness ratio was US$426 per DALY averted from the government perspective and US$400 per DALY averted from the societal perspective. The estimated annual cost of the HPV vaccination program (US$3,343,311) represents approximately 3.53% of the country′s total immunization budget for 2018 or 0.13% of total health expenditures. In Afghanistan, HPV vaccine introduction targeting a single cohort is potentially cost-effective (0.7 times the GDP per capita of $586) from both the government and societal perspective with additional health benefits generated by a catch-up campaign, depending on the government′s willingness to pay for the projected health outcomes."}},{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"bibliographic","authorship":[{"display_name":"Anwari Palwasha","position":"first"},{"display_name":"Debellut Frédéric","position":"middle"},{"display_name":"Vodicka Elisabeth","position":"middle"},{"display_name":"Clark Andrew","position":"middle"},{"display_name":"Farewar Farhad","position":"middle"},{"display_name":"Zhwak Zubiada","position":"middle"},{"display_name":"Nazary Dastagger","position":"middle"},{"display_name":"Pecenka Clint","position":"middle"},{"display_name":"LaMontagne D","position":"middle"},{"display_name":"Safi Najibullah","position":"last"}],"publication_year":2019,"publisher":"Elsevier BV","title":"Potential health impact and cost-effectiveness of bivalent human papillomavirus (HPV) vaccination in Afghanistan"}}]}
4
+ {"visibility":"public","identifiers":[{"identifier":"10.1016/j.cct.2021.106266","identifier_type":"doi"}],"enhancements":[{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"abstract","process":"other","abstract":"Human papillomavirus (HPV) infection is the primary cause of cervical cancer. In 2018, the World Health Organization (WHO) Director General announced his commitment to eliminate cervical cancer, with HPV vaccination as a priority. However, the costs of setting up a multi-dose HPV vaccination programme remain a barrier to its introduction. We are conducting a randomised-controlled trial of reduced dose schedules of HPV vaccine in Tanzania to establish whether a single dose produces immune responses that will be effective in preventing cervical cancer. 930 girls aged 9–14 years in Mwanza, Tanzania, were randomised to one of 6 arms, comprising 3 different dose schedules of the 2-valent (Cervarix) and 9-valent (Gardasil-9) HPV vaccines: 3 doses; 2 doses given 6 months apart; or a single dose. All participants will be followed for 36 months; those in the 1 and 2 dose arms will be followed for 60 months. Trial outcomes focus on vaccine immune responses including HPV 16/18-specific antibody levels, antibody avidity, and memory B cell responses. Results will be immunobridged to historical cohorts of girls and young women in whom efficacy has been demonstrated. This is the first randomised trial of the single dose HPV vaccine schedule in the target age group. The trial will allow us to examine the quality and durability of immune responses of reduced dose schedules in a population with high burden of malaria and other infections that may affect vaccine immune responses. Initial results (24 months) are expected to be published in early 2021."}},{"source":"destiny_sdk.eppi_parser@1.0","visibility":"public","content":{"enhancement_type":"bibliographic","authorship":[{"display_name":"Baisley Kathy","position":"first"},{"display_name":"Whitworth Hilary","position":"middle"},{"display_name":"Changalucha John","position":"middle"},{"display_name":"Pinto Lígia","position":"middle"},{"display_name":"Dillner Joakim","position":"middle"},{"display_name":"Kapiga Saidi","position":"middle"},{"display_name":"de Sanjosé Sílvia","position":"middle"},{"display_name":"Mayaud Philippe","position":"middle"},{"display_name":"Hayes Richard","position":"middle"},{"display_name":"Lacey Charles","position":"middle"},{"display_name":"Watson‐Jones Deborah","position":"last"}],"publication_year":2021,"publisher":"Elsevier BV","title":"A dose-reduction HPV vaccine immunobridging trial of two HPV vaccines among adolescent girls in Tanzania (the DoRIS trial) – Study protocol for a randomised controlled trial"}}]}