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.
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/.gitignore +3 -2
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/PKG-INFO +1 -1
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/pyproject.toml +1 -1
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/client.py +3 -2
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/enhancements.py +27 -37
- destiny_sdk-0.5.1/src/destiny_sdk/identifiers.py +192 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/imports.py +23 -73
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/parsers/eppi_parser.py +0 -1
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/robots.py +11 -19
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/visibility.py +3 -8
- destiny_sdk-0.5.1/tests/unit/test_data/eppi_import.jsonl +4 -0
- destiny_sdk-0.5.1/tests/unit/test_data/eppi_import_with_annotations.jsonl +4 -0
- destiny_sdk-0.5.1/tests/unit/test_identifiers.py +320 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_robots.py +1 -3
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/uv.lock +1 -1
- destiny_sdk-0.4.1/src/destiny_sdk/identifiers.py +0 -110
- destiny_sdk-0.4.1/tests/unit/test_data/eppi_import.jsonl +0 -4
- destiny_sdk-0.4.1/tests/unit/test_data/eppi_import_with_annotations.jsonl +0 -4
- destiny_sdk-0.4.1/tests/unit/test_identifiers.py +0 -104
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/LICENSE +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/README.md +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/__init__.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/auth.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/core.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/parsers/__init__.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/py.typed +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/src/destiny_sdk/references.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/__init__.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/conftest.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/parsers/test_eppi_parser.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_auth.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_client.py +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_data/eppi_report.json +0 -0
- {destiny_sdk-0.4.1 → destiny_sdk-0.5.1}/tests/unit/test_enhancements.py +0 -0
- {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
|
-
#
|
|
190
|
-
|
|
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.
|
|
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
|
|
@@ -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):
|
|
@@ -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"}}]}
|