destiny_sdk 0.5.1__py3-none-any.whl → 0.6.0__py3-none-any.whl
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/__init__.py +2 -0
- destiny_sdk/core.py +14 -1
- destiny_sdk/labs/__init__.py +10 -0
- destiny_sdk/labs/references.py +154 -0
- destiny_sdk/references.py +9 -1
- destiny_sdk/search.py +52 -0
- {destiny_sdk-0.5.1.dist-info → destiny_sdk-0.6.0.dist-info}/METADATA +10 -2
- {destiny_sdk-0.5.1.dist-info → destiny_sdk-0.6.0.dist-info}/RECORD +10 -7
- {destiny_sdk-0.5.1.dist-info → destiny_sdk-0.6.0.dist-info}/WHEEL +0 -0
- {destiny_sdk-0.5.1.dist-info → destiny_sdk-0.6.0.dist-info}/licenses/LICENSE +0 -0
destiny_sdk/__init__.py
CHANGED
destiny_sdk/core.py
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Self
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from destiny_sdk.search import SearchResultPage, SearchResultTotal
|
|
6
8
|
|
|
7
9
|
# These are non-standard newline characters that are not escaped by model_dump_json().
|
|
8
10
|
# We want jsonl files to have empirical new lines so they can be streamed line by line.
|
|
@@ -47,3 +49,14 @@ class _JsonlFileInputMixIn(BaseModel):
|
|
|
47
49
|
:rtype: Self
|
|
48
50
|
"""
|
|
49
51
|
return cls.model_validate_json(jsonl)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SearchResultMixIn(BaseModel):
|
|
55
|
+
"""A mixin class for models that represent search results."""
|
|
56
|
+
|
|
57
|
+
total: SearchResultTotal = Field(
|
|
58
|
+
description="The total number of results matching the search criteria.",
|
|
59
|
+
)
|
|
60
|
+
page: SearchResultPage = Field(
|
|
61
|
+
description="Information about the page of results.",
|
|
62
|
+
)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Extended Reference SDK.
|
|
3
|
+
|
|
4
|
+
Extended Reference class for the Destiny SDK
|
|
5
|
+
with added experimental convenience methods and properties.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from collections.abc import Generator
|
|
9
|
+
from typing import cast
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
from destiny_sdk.enhancements import (
|
|
14
|
+
Annotation,
|
|
15
|
+
AnnotationType,
|
|
16
|
+
BibliographicMetadataEnhancement,
|
|
17
|
+
EnhancementType,
|
|
18
|
+
)
|
|
19
|
+
from destiny_sdk.identifiers import ExternalIdentifierType
|
|
20
|
+
from destiny_sdk.references import Reference
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LabsReference(BaseModel):
|
|
24
|
+
"""Experimental presenter class for Reference with added convenience methods."""
|
|
25
|
+
|
|
26
|
+
reference: Reference = Field(
|
|
27
|
+
...,
|
|
28
|
+
description="The core Reference object",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def _get_id(self, identifier_type: ExternalIdentifierType) -> str | int | None:
|
|
32
|
+
"""Fetch an identifier matching the given identifier_type."""
|
|
33
|
+
for identifier in self.reference.identifiers or []:
|
|
34
|
+
if identifier.identifier_type == identifier_type:
|
|
35
|
+
return identifier.identifier
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def openalex_id(self) -> str | None:
|
|
40
|
+
"""Return an OpenAlex ID for the reference."""
|
|
41
|
+
return cast(
|
|
42
|
+
str | None, self._get_id(identifier_type=ExternalIdentifierType.OPEN_ALEX)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def doi(self) -> str | None:
|
|
47
|
+
"""Return a DOI for the reference."""
|
|
48
|
+
return cast(
|
|
49
|
+
str | None, self._get_id(identifier_type=ExternalIdentifierType.DOI)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def pubmed_id(self) -> int | None:
|
|
54
|
+
"""Return a pubmed ID for the reference."""
|
|
55
|
+
return cast(
|
|
56
|
+
int | None, self._get_id(identifier_type=ExternalIdentifierType.PM_ID)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def abstract(self) -> str | None:
|
|
61
|
+
"""Return an abstract for the reference."""
|
|
62
|
+
for enhancement in self.reference.enhancements or []:
|
|
63
|
+
if enhancement.content.enhancement_type == EnhancementType.ABSTRACT:
|
|
64
|
+
return enhancement.content.abstract
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def publication_year(self) -> int | None:
|
|
69
|
+
"""Return a publication year for the reference."""
|
|
70
|
+
for meta in self.it_bibliographics():
|
|
71
|
+
if meta.publication_year is not None:
|
|
72
|
+
return meta.publication_year
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def title(self) -> str | None:
|
|
77
|
+
"""The title of the reference. If multiple are present, return first one."""
|
|
78
|
+
for meta in self.it_bibliographics():
|
|
79
|
+
if meta.title is not None:
|
|
80
|
+
return meta.title
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
def it_bibliographics(
|
|
84
|
+
self,
|
|
85
|
+
) -> Generator[BibliographicMetadataEnhancement, None, None]:
|
|
86
|
+
"""Iterate bibliographic enhancements."""
|
|
87
|
+
for enhancement in self.reference.enhancements or []:
|
|
88
|
+
if enhancement.content.enhancement_type == EnhancementType.BIBLIOGRAPHIC:
|
|
89
|
+
yield enhancement.content
|
|
90
|
+
|
|
91
|
+
def it_annotations(
|
|
92
|
+
self,
|
|
93
|
+
source: str | None = None,
|
|
94
|
+
annotation_type: AnnotationType | None = None,
|
|
95
|
+
scheme: str | None = None,
|
|
96
|
+
label: str | None = None,
|
|
97
|
+
) -> Generator[Annotation, None, None]:
|
|
98
|
+
"""
|
|
99
|
+
Iterate annotation enhancements for the given filters.
|
|
100
|
+
|
|
101
|
+
:param source: Optional filter for Enhancement.source
|
|
102
|
+
:param annotation_type: Optional filter for
|
|
103
|
+
AnnotationEnhancement.annotation_type
|
|
104
|
+
:param scheme: Optional filter for Annotation.scheme
|
|
105
|
+
:param label: Optional filter for Annotation.label
|
|
106
|
+
"""
|
|
107
|
+
for enhancement in self.reference.enhancements or []:
|
|
108
|
+
if enhancement.content.enhancement_type == EnhancementType.ANNOTATION:
|
|
109
|
+
if source is not None and enhancement.source != source:
|
|
110
|
+
continue
|
|
111
|
+
for annotation in enhancement.content.annotations:
|
|
112
|
+
if (
|
|
113
|
+
annotation_type is not None
|
|
114
|
+
and annotation.annotation_type != annotation_type
|
|
115
|
+
):
|
|
116
|
+
continue
|
|
117
|
+
if scheme is not None and annotation.scheme != scheme:
|
|
118
|
+
continue
|
|
119
|
+
if label is not None and annotation.label != label:
|
|
120
|
+
continue
|
|
121
|
+
yield annotation
|
|
122
|
+
|
|
123
|
+
def has_bool_annotation(
|
|
124
|
+
self,
|
|
125
|
+
source: str | None = None,
|
|
126
|
+
scheme: str | None = None,
|
|
127
|
+
label: str | None = None,
|
|
128
|
+
expected_value: bool = True, # noqa: FBT001, FBT002
|
|
129
|
+
) -> bool | None:
|
|
130
|
+
"""
|
|
131
|
+
Check if a specific annotation exists and is true.
|
|
132
|
+
|
|
133
|
+
:param source: Optional filter for Enhancement.source
|
|
134
|
+
:param scheme: Optional filter for Annotation.scheme
|
|
135
|
+
:param label: Optional filter for Annotation.label
|
|
136
|
+
:param expected_value: Specify expected boolean annotation value
|
|
137
|
+
:return: Returns the boolean value for the first annotation matching
|
|
138
|
+
the filters or None if nothing is found.
|
|
139
|
+
"""
|
|
140
|
+
if scheme is None and label is None:
|
|
141
|
+
msg = "Please use at least one of the optional scheme or label filters."
|
|
142
|
+
raise AssertionError(msg)
|
|
143
|
+
|
|
144
|
+
found_annotation = False
|
|
145
|
+
for annotation in self.it_annotations(
|
|
146
|
+
source=source,
|
|
147
|
+
annotation_type=AnnotationType.BOOLEAN,
|
|
148
|
+
scheme=scheme,
|
|
149
|
+
label=label,
|
|
150
|
+
):
|
|
151
|
+
if annotation.value == expected_value:
|
|
152
|
+
return True
|
|
153
|
+
found_annotation = True
|
|
154
|
+
return False if found_annotation else None
|
destiny_sdk/references.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Self
|
|
|
4
4
|
|
|
5
5
|
from pydantic import UUID4, BaseModel, Field, TypeAdapter
|
|
6
6
|
|
|
7
|
-
from destiny_sdk.core import _JsonlFileInputMixIn
|
|
7
|
+
from destiny_sdk.core import SearchResultMixIn, _JsonlFileInputMixIn
|
|
8
8
|
from destiny_sdk.enhancements import Enhancement, EnhancementFileInput
|
|
9
9
|
from destiny_sdk.identifiers import ExternalIdentifier
|
|
10
10
|
from destiny_sdk.visibility import Visibility
|
|
@@ -65,3 +65,11 @@ class ReferenceFileInput(_JsonlFileInputMixIn, BaseModel):
|
|
|
65
65
|
default=None,
|
|
66
66
|
description="A list of enhancements for the reference",
|
|
67
67
|
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ReferenceSearchResult(SearchResultMixIn, BaseModel):
|
|
71
|
+
"""A search result for references."""
|
|
72
|
+
|
|
73
|
+
references: list[Reference] = Field(
|
|
74
|
+
description="The references returned by the search.",
|
|
75
|
+
)
|
destiny_sdk/search.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Models for search queries and results."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SearchResultTotal(BaseModel):
|
|
7
|
+
"""Information about the total number of search results."""
|
|
8
|
+
|
|
9
|
+
count: int = Field(
|
|
10
|
+
description="The total number of results matching the search criteria.",
|
|
11
|
+
)
|
|
12
|
+
is_lower_bound: bool = Field(
|
|
13
|
+
description="Whether the count is a lower bound (true) or exact (false).",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SearchResultPage(BaseModel):
|
|
18
|
+
"""Information about the page of search results."""
|
|
19
|
+
|
|
20
|
+
count: int = Field(
|
|
21
|
+
description="The number of results on this page.",
|
|
22
|
+
)
|
|
23
|
+
number: int = Field(
|
|
24
|
+
description="The page number of results returned, indexed from 1.",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AnnotationFilter(BaseModel):
|
|
29
|
+
"""An annotation filter for search queries."""
|
|
30
|
+
|
|
31
|
+
scheme: str = Field(
|
|
32
|
+
description="The annotation scheme to filter by.",
|
|
33
|
+
)
|
|
34
|
+
label: str | None = Field(
|
|
35
|
+
None,
|
|
36
|
+
description="The annotation label to filter by.",
|
|
37
|
+
)
|
|
38
|
+
score: float | None = Field(
|
|
39
|
+
None,
|
|
40
|
+
description="The minimum score for the annotation filter.",
|
|
41
|
+
ge=0.0,
|
|
42
|
+
le=1.0,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def serialize(self) -> str:
|
|
46
|
+
"""Serialize the annotation filter to a string."""
|
|
47
|
+
annotation = self.scheme
|
|
48
|
+
if self.label:
|
|
49
|
+
annotation += f"/{self.label}"
|
|
50
|
+
if self.score is not None:
|
|
51
|
+
annotation += f"@{self.score}"
|
|
52
|
+
return annotation
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: destiny_sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: A software development kit (sdk) to support interaction with the DESTINY repository
|
|
5
|
-
Author-email: Adam Hamilton <adam@futureevidence.org>, Andrew Harvey <andrew@futureevidence.org>, Daniel Breves <daniel@futureevidence.org>, Jack Walmisley <jack@futureevidence.org>
|
|
5
|
+
Author-email: Adam Hamilton <adam@futureevidence.org>, Andrew Harvey <andrew@futureevidence.org>, Daniel Breves <daniel@futureevidence.org>, Jack Walmisley <jack@futureevidence.org>, Tim Repke <tim.repke@pik-potsdam.de>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Python: ~=3.12
|
|
@@ -14,6 +14,7 @@ Requires-Dist: pytest-asyncio<2,>=1.0.0
|
|
|
14
14
|
Requires-Dist: pytest-httpx<0.36,>=0.35.0
|
|
15
15
|
Requires-Dist: pytest<9,>=8.4.0
|
|
16
16
|
Requires-Dist: python-jose<4,>=3.4.0
|
|
17
|
+
Provides-Extra: labs
|
|
17
18
|
Description-Content-Type: text/markdown
|
|
18
19
|
|
|
19
20
|
# DESTINY SDK
|
|
@@ -34,6 +35,13 @@ pip install destiny-sdk
|
|
|
34
35
|
uv add destiny-sdk
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
Some labs functionality may require extra dependencies - these can be installed by:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip install destiny-sdk[labs]
|
|
42
|
+
uv add destiny-sdk --extra labs
|
|
43
|
+
```
|
|
44
|
+
|
|
37
45
|
## Development
|
|
38
46
|
|
|
39
47
|
### Dependencies
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
destiny_sdk/__init__.py,sha256=
|
|
1
|
+
destiny_sdk/__init__.py,sha256=NdSlsPQyDF3TW30_JzbvYMRBRA9iT677iTRWWCMdYOA,382
|
|
2
2
|
destiny_sdk/auth.py,sha256=bY72ywZEcG_67YBd9PrwgWTXkCf58rhLvVEXrtXbWtA,6247
|
|
3
3
|
destiny_sdk/client.py,sha256=fTBtuq5emT8ieNtCuCY8Y6xAKZJDLq8sG1WOvmjLz-I,4971
|
|
4
|
-
destiny_sdk/core.py,sha256=
|
|
4
|
+
destiny_sdk/core.py,sha256=PYCYpY72MHXo7iQMHtnXcnCOGn6CUsbYoykHvtQl4Oc,1857
|
|
5
5
|
destiny_sdk/enhancements.py,sha256=SkIlIlWKBN7Z-aXpQiy22SXrU7zVnKxaRb4F5yaFsO8,11503
|
|
6
6
|
destiny_sdk/identifiers.py,sha256=1N2cszBmnQoUeKm54-7MUTO-zTDuvW8U9OjTeAmhWvo,7182
|
|
7
7
|
destiny_sdk/imports.py,sha256=b-rh-dt3NsyLGxqmVzIzKaHiXhbw-3wtAaBN-ZW-i1E,5940
|
|
8
8
|
destiny_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
destiny_sdk/references.py,sha256=
|
|
9
|
+
destiny_sdk/references.py,sha256=3Y8gBMTSyZY35S3pB1bnVHMai9RRiGeoGZysNvSo7kk,2553
|
|
10
10
|
destiny_sdk/robots.py,sha256=I_ZvMxwST52e8ovhv0-gPbOB3P9tptbRG0LrkNNOqKo,13463
|
|
11
|
+
destiny_sdk/search.py,sha256=LIPj_h0yMnav_Stp4qRLg1PvZa6h3BV4N2bXwAYZDqA,1447
|
|
11
12
|
destiny_sdk/visibility.py,sha256=8D44Q868YdScAt6eAFgXXrhonozXnv_Qa5w5yEGMPX8,577
|
|
13
|
+
destiny_sdk/labs/__init__.py,sha256=H4RFPyeelqZ56PagnWPX-JZeWlxPnCZoYHtr4c9SU9Q,180
|
|
14
|
+
destiny_sdk/labs/references.py,sha256=iZisRgGZ5c7X7uTFoe6Q0AwwFMa4yJbIoPUVv_hvOiU,5589
|
|
12
15
|
destiny_sdk/parsers/__init__.py,sha256=d5gS--bXla_0I7e_9wTBnGWMXt2U8b-_ndeprTPe1hk,149
|
|
13
16
|
destiny_sdk/parsers/eppi_parser.py,sha256=rEOtt_5Kp3oktFlzRTLZ2x4_7aQ9-ba3FYpkaEnpnvs,5521
|
|
14
|
-
destiny_sdk-0.
|
|
15
|
-
destiny_sdk-0.
|
|
16
|
-
destiny_sdk-0.
|
|
17
|
-
destiny_sdk-0.
|
|
17
|
+
destiny_sdk-0.6.0.dist-info/METADATA,sha256=_xLg34LzOiBZmcjmg6-P6bTKmHIBnArl-0k-Qndftwc,2657
|
|
18
|
+
destiny_sdk-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
19
|
+
destiny_sdk-0.6.0.dist-info/licenses/LICENSE,sha256=6QURU4gvvTjVZ5rfp5amZ6FtFvcpPhAGUjxF5WSZAHI,9138
|
|
20
|
+
destiny_sdk-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|