adsfolio 0.5.1__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.
- adsfolio/__init__.py +16 -0
- adsfolio/api/__init__.py +9 -0
- adsfolio/api/export.py +293 -0
- adsfolio/api/journals.py +138 -0
- adsfolio/api/libraries.py +505 -0
- adsfolio/api/metrics.py +151 -0
- adsfolio/api/search.py +324 -0
- adsfolio/bibtex/__init__.py +22 -0
- adsfolio/bibtex/formatter.py +105 -0
- adsfolio/bibtex/io.py +118 -0
- adsfolio/bibtex/parser.py +287 -0
- adsfolio/bibtex/sanitize.py +188 -0
- adsfolio/citations.py +579 -0
- adsfolio/cli/__init__.py +12 -0
- adsfolio/cli/__main__.py +5 -0
- adsfolio/cli/commands.py +926 -0
- adsfolio/client.py +762 -0
- adsfolio/config.py +478 -0
- adsfolio/cv/__init__.py +70 -0
- adsfolio/cv/cli.py +303 -0
- adsfolio/cv/config.py +442 -0
- adsfolio/cv/config_starter.py +108 -0
- adsfolio/cv/query.py +103 -0
- adsfolio/cv/render_factory.py +43 -0
- adsfolio/cv/render_legend.py +64 -0
- adsfolio/cv/renderer.py +396 -0
- adsfolio/cv/template.py +372 -0
- adsfolio/data_migration.py +119 -0
- adsfolio/formatting.py +396 -0
- adsfolio/gui/__init__.py +1 -0
- adsfolio/gui/about.py +200 -0
- adsfolio/gui/ads_library_dialog.py +681 -0
- adsfolio/gui/ads_library_export_dialog.py +382 -0
- adsfolio/gui/ads_library_jobs.py +269 -0
- adsfolio/gui/annotation_merge.py +529 -0
- adsfolio/gui/app.py +55 -0
- adsfolio/gui/appearance.py +626 -0
- adsfolio/gui/assets/adsfolio.icns +0 -0
- adsfolio/gui/assets/adsfolio.ico +0 -0
- adsfolio/gui/assets/adsfolio.png +0 -0
- adsfolio/gui/assets/adsfolio.svg +108 -0
- adsfolio/gui/citation_styles.py +637 -0
- adsfolio/gui/config.py +483 -0
- adsfolio/gui/cv_dialog.py +1160 -0
- adsfolio/gui/detail_pane.py +1660 -0
- adsfolio/gui/dialogs.py +1014 -0
- adsfolio/gui/icons.py +157 -0
- adsfolio/gui/jobs.py +196 -0
- adsfolio/gui/library.py +321 -0
- adsfolio/gui/main_window.py +3583 -0
- adsfolio/gui/models.py +562 -0
- adsfolio/gui/notifications.py +230 -0
- adsfolio/gui/osint.py +297 -0
- adsfolio/gui/references_dialog.py +435 -0
- adsfolio/gui/refresh_job.py +82 -0
- adsfolio/gui/token_dialog.py +186 -0
- adsfolio/gui/update_dialog.py +382 -0
- adsfolio/gui/updates.py +538 -0
- adsfolio/gui/watch.py +699 -0
- adsfolio/gui/welcome.py +149 -0
- adsfolio/journal_utils.py +272 -0
- adsfolio/library_sync.py +531 -0
- adsfolio/pdf/__init__.py +38 -0
- adsfolio/pdf/annotation_merge.py +771 -0
- adsfolio/pdf/annotations.py +393 -0
- adsfolio/pdf/arxiv.py +85 -0
- adsfolio/pdf/catalog.py +2008 -0
- adsfolio/pdf/cli.py +2267 -0
- adsfolio/pdf/cli_common.py +99 -0
- adsfolio/pdf/cli_upgrade.py +81 -0
- adsfolio/pdf/doi_providers.py +436 -0
- adsfolio/pdf/files.py +70 -0
- adsfolio/pdf/identifier.py +1694 -0
- adsfolio/pdf/identify.py +731 -0
- adsfolio/pdf/identify_settings.py +316 -0
- adsfolio/pdf/obsidian.py +208 -0
- adsfolio/pdf/prefetch.py +324 -0
- adsfolio/pdf/readiness.py +109 -0
- adsfolio/pdf/registry.py +792 -0
- adsfolio/pdf/rename_settings.py +187 -0
- adsfolio/pdf/renamer.py +454 -0
- adsfolio/pdf/sidecar.py +420 -0
- adsfolio/pdf/tracked_move.py +150 -0
- adsfolio/pdf/upgrade.py +364 -0
- adsfolio/pdf/upgrade_utils.py +318 -0
- adsfolio/pdf/watch_session.py +738 -0
- adsfolio/pdf/watcher.py +676 -0
- adsfolio/pubcache.py +146 -0
- adsfolio/reference_cli.py +230 -0
- adsfolio/references.py +438 -0
- adsfolio/user_profile.py +225 -0
- adsfolio-0.5.1.dist-info/METADATA +168 -0
- adsfolio-0.5.1.dist-info/RECORD +97 -0
- adsfolio-0.5.1.dist-info/WHEEL +5 -0
- adsfolio-0.5.1.dist-info/entry_points.txt +3 -0
- adsfolio-0.5.1.dist-info/licenses/LICENSE +28 -0
- adsfolio-0.5.1.dist-info/top_level.txt +1 -0
adsfolio/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""ADSfolio research library tools and NASA ADS client."""
|
|
2
|
+
|
|
3
|
+
from .client import ADSClient
|
|
4
|
+
from .config import Config
|
|
5
|
+
from .formatting import format_author_list_cv, format_title_latex
|
|
6
|
+
from .journal_utils import format_journal
|
|
7
|
+
|
|
8
|
+
__version__ = "0.5.1"
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ADSClient",
|
|
11
|
+
"Config",
|
|
12
|
+
"__version__",
|
|
13
|
+
"format_author_list_cv",
|
|
14
|
+
"format_journal",
|
|
15
|
+
"format_title_latex",
|
|
16
|
+
]
|
adsfolio/api/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""API module exports."""
|
|
2
|
+
|
|
3
|
+
from .export import ExportAPI
|
|
4
|
+
from .journals import JournalsAPI
|
|
5
|
+
from .libraries import LibrariesAPI
|
|
6
|
+
from .metrics import MetricsAPI
|
|
7
|
+
from .search import SearchAPI
|
|
8
|
+
|
|
9
|
+
__all__ = ["ExportAPI", "JournalsAPI", "LibrariesAPI", "MetricsAPI", "SearchAPI"]
|
adsfolio/api/export.py
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Export API wrapper for ADS.
|
|
3
|
+
|
|
4
|
+
Provides access to bibliography export in various formats:
|
|
5
|
+
- BibTeX, AASTeX, MNRAS, Icarus, etc.
|
|
6
|
+
- Custom format strings
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Callable, Sequence
|
|
10
|
+
from concurrent.futures import CancelledError
|
|
11
|
+
from typing import TYPE_CHECKING, ClassVar
|
|
12
|
+
|
|
13
|
+
from ..bibtex.sanitize import BIBTEX_FORMATS, repair_bibtex
|
|
14
|
+
from ..config import ADSError
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ..client import BaseClient
|
|
18
|
+
|
|
19
|
+
__all__ = ["ExportAPI"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ExportAPI:
|
|
23
|
+
"""
|
|
24
|
+
Export API for retrieving bibliographic data in various formats.
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
--------
|
|
28
|
+
>>> api = ExportAPI(client)
|
|
29
|
+
>>> bibtex = api.export(["2020ApJ...900..100S"], format="bibtex")
|
|
30
|
+
>>> print(bibtex)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
# ADS export_service/config.py specifies a maximum of 2,000 Solr records
|
|
34
|
+
# per request. Keep this separate from the library pagination limit.
|
|
35
|
+
MAX_RECORDS_PER_REQUEST = 2000
|
|
36
|
+
BIBLIOGRAPHY_FORMATS = (
|
|
37
|
+
"bibtex",
|
|
38
|
+
"bibtexabs",
|
|
39
|
+
"ris",
|
|
40
|
+
"endnote",
|
|
41
|
+
"aastex",
|
|
42
|
+
"mnras",
|
|
43
|
+
"icarus",
|
|
44
|
+
"soph",
|
|
45
|
+
"ads",
|
|
46
|
+
"procite",
|
|
47
|
+
"refworks",
|
|
48
|
+
"medlars",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Supported export formats
|
|
52
|
+
FORMATS: ClassVar[list[str]] = [
|
|
53
|
+
"bibtex", # Standard BibTeX
|
|
54
|
+
"bibtexabs", # BibTeX with abstracts
|
|
55
|
+
"ads", # ADS format
|
|
56
|
+
"aastex", # AASTeX format
|
|
57
|
+
"icarus", # Icarus journal format
|
|
58
|
+
"mnras", # MNRAS format
|
|
59
|
+
"soph", # Solar Physics format
|
|
60
|
+
"endnote", # EndNote format
|
|
61
|
+
"procite", # ProCite format
|
|
62
|
+
"ris", # RIS format
|
|
63
|
+
"refworks", # RefWorks format
|
|
64
|
+
"rss", # RSS format
|
|
65
|
+
"medlars", # MEDLARS format
|
|
66
|
+
"dcxml", # Dublin Core XML
|
|
67
|
+
"refxml", # Reference XML
|
|
68
|
+
"refabsxml", # Reference with abstract XML
|
|
69
|
+
"votable", # VOTable format
|
|
70
|
+
"custom", # Custom format string
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
def __init__(self, client: "BaseClient"):
|
|
74
|
+
self._client = client
|
|
75
|
+
|
|
76
|
+
def export(
|
|
77
|
+
self,
|
|
78
|
+
bibcodes: list[str],
|
|
79
|
+
format: str = "bibtex",
|
|
80
|
+
sort: str | None = None,
|
|
81
|
+
custom_format: str | None = None,
|
|
82
|
+
) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Export bibcodes in the specified format.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
bibcodes : list[str]
|
|
89
|
+
List of bibcodes to export.
|
|
90
|
+
format : str, optional
|
|
91
|
+
Export format. Default: "bibtex".
|
|
92
|
+
See FORMATS for available options.
|
|
93
|
+
sort : str, optional
|
|
94
|
+
Sort order, e.g., "first_author asc".
|
|
95
|
+
custom_format : str, optional
|
|
96
|
+
Custom format string (required if format="custom").
|
|
97
|
+
See ADS documentation for format codes.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
str
|
|
102
|
+
Exported bibliography in requested format.
|
|
103
|
+
|
|
104
|
+
Examples
|
|
105
|
+
--------
|
|
106
|
+
>>> bibtex = api.export(["2020ApJ...900..100S"], format="bibtex")
|
|
107
|
+
>>> custom = api.export(bibcodes, format="custom",
|
|
108
|
+
... custom_format="%R %T")
|
|
109
|
+
"""
|
|
110
|
+
payload = {"bibcode": bibcodes}
|
|
111
|
+
if sort:
|
|
112
|
+
payload["sort"] = sort
|
|
113
|
+
if format == "custom" or format not in self.FORMATS or custom_format:
|
|
114
|
+
format_string = custom_format or (format if format != "custom" else "")
|
|
115
|
+
if not format_string.strip():
|
|
116
|
+
raise ValueError("custom_format is required for a custom export.")
|
|
117
|
+
payload["format"] = format_string
|
|
118
|
+
endpoint = "/export/custom"
|
|
119
|
+
else:
|
|
120
|
+
endpoint = f"/export/{format}"
|
|
121
|
+
|
|
122
|
+
response = self._client.post(endpoint, data=payload)
|
|
123
|
+
text = response.get("export", "")
|
|
124
|
+
if endpoint == f"/export/{format}" and format in BIBTEX_FORMATS:
|
|
125
|
+
text = repair_bibtex(text)
|
|
126
|
+
return text
|
|
127
|
+
|
|
128
|
+
def export_complete(
|
|
129
|
+
self,
|
|
130
|
+
bibcodes: Sequence[str],
|
|
131
|
+
format: str = "bibtex",
|
|
132
|
+
*,
|
|
133
|
+
cancelled: Callable[[], bool] | None = None,
|
|
134
|
+
) -> str:
|
|
135
|
+
"""Export every requested record as one bibliography, or fail.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
bibcodes : sequence of str
|
|
140
|
+
Unique ADS Bibcodes. The input order is preserved across batches.
|
|
141
|
+
format : str, optional
|
|
142
|
+
A format in `BIBLIOGRAPHY_FORMATS`. Defaults to ``bibtex``.
|
|
143
|
+
XML and custom formats require different assembly rules and are
|
|
144
|
+
supported separately by ``export()``.
|
|
145
|
+
cancelled : callable, optional
|
|
146
|
+
Cancellation check before and after each network request.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
str
|
|
151
|
+
Complete bibliography. An empty input returns an empty string.
|
|
152
|
+
|
|
153
|
+
Raises
|
|
154
|
+
------
|
|
155
|
+
ValueError
|
|
156
|
+
If the format is unsupported or Bibcodes are empty or duplicated.
|
|
157
|
+
TypeError
|
|
158
|
+
If `bibcodes` is a string rather than a sequence of identifiers.
|
|
159
|
+
ADSError
|
|
160
|
+
If ADS returns missing, repeated, or malformed records.
|
|
161
|
+
concurrent.futures.CancelledError
|
|
162
|
+
If cancellation is requested. No partial bibliography is returned.
|
|
163
|
+
"""
|
|
164
|
+
if format not in self.BIBLIOGRAPHY_FORMATS:
|
|
165
|
+
raise ValueError(f"Unsupported bibliography format: {format}")
|
|
166
|
+
if isinstance(bibcodes, str):
|
|
167
|
+
raise TypeError("Pass a sequence of Bibcodes, not a single string.")
|
|
168
|
+
codes = tuple(code.strip() for code in bibcodes)
|
|
169
|
+
if any(not code for code in codes) or len(
|
|
170
|
+
{code.lower() for code in codes}
|
|
171
|
+
) != len(codes):
|
|
172
|
+
raise ValueError("Bibcodes must be non-empty and unique.")
|
|
173
|
+
references: list[str] = []
|
|
174
|
+
wrappers: tuple[str, str] | None = None
|
|
175
|
+
for start in range(0, len(codes), self.MAX_RECORDS_PER_REQUEST):
|
|
176
|
+
if cancelled is not None and cancelled():
|
|
177
|
+
raise CancelledError("Bibliography export cancelled.")
|
|
178
|
+
batch = codes[start : start + self.MAX_RECORDS_PER_REQUEST]
|
|
179
|
+
response = self._client.post(
|
|
180
|
+
f"/export/{format}",
|
|
181
|
+
data={"bibcode": list(batch), "sort": "no sort", "outputformat": 2},
|
|
182
|
+
)
|
|
183
|
+
if cancelled is not None and cancelled():
|
|
184
|
+
raise CancelledError("Bibliography export cancelled.")
|
|
185
|
+
documents = response.get("docs")
|
|
186
|
+
count = response.get("num_docs")
|
|
187
|
+
if (
|
|
188
|
+
type(count) is not int
|
|
189
|
+
or count != len(batch)
|
|
190
|
+
or not isinstance(documents, list)
|
|
191
|
+
or len(documents) != len(batch)
|
|
192
|
+
):
|
|
193
|
+
raise ADSError(
|
|
194
|
+
"ADS returned an incomplete bibliography; no file was saved."
|
|
195
|
+
)
|
|
196
|
+
by_bibcode: dict[str, str] = {}
|
|
197
|
+
for document in documents:
|
|
198
|
+
if not isinstance(document, dict):
|
|
199
|
+
raise ADSError("ADS returned a malformed bibliography record.")
|
|
200
|
+
code = document.get("bibcode")
|
|
201
|
+
reference = document.get("reference")
|
|
202
|
+
if (
|
|
203
|
+
not isinstance(code, str)
|
|
204
|
+
or not isinstance(reference, str)
|
|
205
|
+
or not reference.strip()
|
|
206
|
+
or code.lower() in by_bibcode
|
|
207
|
+
):
|
|
208
|
+
raise ADSError(
|
|
209
|
+
"ADS returned a missing or repeated bibliography record."
|
|
210
|
+
)
|
|
211
|
+
if format in BIBTEX_FORMATS:
|
|
212
|
+
reference = repair_bibtex(reference)
|
|
213
|
+
by_bibcode[code.lower()] = reference
|
|
214
|
+
if set(by_bibcode) != {code.lower() for code in batch}:
|
|
215
|
+
raise ADSError(
|
|
216
|
+
"ADS bibliography records did not match the requested library."
|
|
217
|
+
)
|
|
218
|
+
header, footer = response.get("header", ""), response.get("footer", "")
|
|
219
|
+
if not isinstance(header, str) or not isinstance(footer, str):
|
|
220
|
+
raise ADSError("ADS returned malformed bibliography headers.")
|
|
221
|
+
if wrappers is not None and wrappers != (header, footer):
|
|
222
|
+
raise ADSError("ADS bibliography headers changed between batches.")
|
|
223
|
+
wrappers = (header, footer)
|
|
224
|
+
references.extend(by_bibcode[code.lower()] for code in batch)
|
|
225
|
+
if not references:
|
|
226
|
+
return ""
|
|
227
|
+
assert wrappers is not None
|
|
228
|
+
return (
|
|
229
|
+
"\n\n".join(
|
|
230
|
+
part.rstrip("\r\n")
|
|
231
|
+
for part in (wrappers[0], *references, wrappers[1])
|
|
232
|
+
if part
|
|
233
|
+
)
|
|
234
|
+
+ "\n"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def bibtex(
|
|
238
|
+
self,
|
|
239
|
+
bibcodes: list[str],
|
|
240
|
+
sort: str | None = None,
|
|
241
|
+
with_abstract: bool = False,
|
|
242
|
+
) -> str:
|
|
243
|
+
"""
|
|
244
|
+
Convenience method for BibTeX export.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
bibcodes : list[str]
|
|
249
|
+
List of bibcodes.
|
|
250
|
+
sort : str, optional
|
|
251
|
+
Sort order.
|
|
252
|
+
with_abstract : bool, optional
|
|
253
|
+
Include abstracts in output. Default: False.
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
str
|
|
258
|
+
BibTeX formatted bibliography.
|
|
259
|
+
"""
|
|
260
|
+
fmt = "bibtexabs" if with_abstract else "bibtex"
|
|
261
|
+
return self.export(bibcodes, format=fmt, sort=sort)
|
|
262
|
+
|
|
263
|
+
def aastex(self, bibcodes: list[str], sort: str | None = None) -> str:
|
|
264
|
+
"""Export in AASTeX format."""
|
|
265
|
+
return self.export(bibcodes, format="aastex", sort=sort)
|
|
266
|
+
|
|
267
|
+
def custom(
|
|
268
|
+
self,
|
|
269
|
+
bibcodes: list[str],
|
|
270
|
+
format_string: str,
|
|
271
|
+
sort: str | None = None,
|
|
272
|
+
) -> str:
|
|
273
|
+
"""
|
|
274
|
+
Export with custom format string.
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
bibcodes : list[str]
|
|
279
|
+
List of bibcodes.
|
|
280
|
+
format_string : str
|
|
281
|
+
Custom format string using ADS format codes.
|
|
282
|
+
Examples: "%R %T", "%m %Y", "%10.5N_%Y_%q_%V_%p"
|
|
283
|
+
sort : str, optional
|
|
284
|
+
Sort order.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
str
|
|
289
|
+
Custom formatted output.
|
|
290
|
+
"""
|
|
291
|
+
return self.export(
|
|
292
|
+
bibcodes, format="custom", custom_format=format_string, sort=sort
|
|
293
|
+
)
|
adsfolio/api/journals.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Journals API wrapper for ADS.
|
|
3
|
+
|
|
4
|
+
Provides access to journal metadata:
|
|
5
|
+
- Summary information by bibstem
|
|
6
|
+
- Journal name search
|
|
7
|
+
- Holdings and reference sources
|
|
8
|
+
- ISSN lookup
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..client import BaseClient
|
|
15
|
+
|
|
16
|
+
__all__ = ["JournalsAPI"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JournalsAPI:
|
|
20
|
+
"""
|
|
21
|
+
Journals API for querying journal metadata.
|
|
22
|
+
|
|
23
|
+
Examples
|
|
24
|
+
--------
|
|
25
|
+
>>> api = JournalsAPI(client)
|
|
26
|
+
>>> summary = api.summary("ApJ")
|
|
27
|
+
>>> print(summary["master"]["journal_name"])
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, client: "BaseClient"):
|
|
31
|
+
self._client = client
|
|
32
|
+
|
|
33
|
+
def summary(self, bibstem: str) -> dict:
|
|
34
|
+
"""
|
|
35
|
+
Get summary information for a journal by bibstem.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
bibstem : str
|
|
40
|
+
Journal bibstem (case-sensitive), e.g., "ApJ", "MNRAS".
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
dict
|
|
45
|
+
Journal summary including master record, identifiers, abbreviations.
|
|
46
|
+
"""
|
|
47
|
+
response = self._client.get(f"/journals/summary/{bibstem}")
|
|
48
|
+
return response.get("summary", {})
|
|
49
|
+
|
|
50
|
+
def search(self, query: str) -> list[dict]:
|
|
51
|
+
"""
|
|
52
|
+
Search for journals by name or abbreviation.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
query : str
|
|
57
|
+
Search text to match against journal names.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
list[dict]
|
|
62
|
+
List of matching journal records.
|
|
63
|
+
"""
|
|
64
|
+
from urllib.parse import quote
|
|
65
|
+
|
|
66
|
+
encoded_query = quote(query)
|
|
67
|
+
response = self._client.get(f"/journals/journal/{encoded_query}")
|
|
68
|
+
return response.get("journal", [])
|
|
69
|
+
|
|
70
|
+
def refsource(self, bibstem: str) -> dict:
|
|
71
|
+
"""
|
|
72
|
+
Get citation data sources by volume for a journal.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
bibstem : str
|
|
77
|
+
Journal bibstem.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
dict
|
|
82
|
+
Reference source data per volume.
|
|
83
|
+
"""
|
|
84
|
+
response = self._client.get(f"/journals/refsource/{bibstem}")
|
|
85
|
+
return response.get("refsource", {})
|
|
86
|
+
|
|
87
|
+
def holdings(self, bibstem: str, volume: str) -> dict:
|
|
88
|
+
"""
|
|
89
|
+
Get available electronic sources for a journal/volume.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
bibstem : str
|
|
94
|
+
Journal bibstem.
|
|
95
|
+
volume : str
|
|
96
|
+
Volume number.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
dict
|
|
101
|
+
Holdings data with available sources.
|
|
102
|
+
"""
|
|
103
|
+
response = self._client.get(f"/journals/holdings/{bibstem}/{volume}")
|
|
104
|
+
return response.get("holdings", {})
|
|
105
|
+
|
|
106
|
+
def issn(self, issn_number: str) -> str | None:
|
|
107
|
+
"""
|
|
108
|
+
Look up bibstem by ISSN.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
issn_number : str
|
|
113
|
+
ISSN identifier (with or without hyphens).
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
str or None
|
|
118
|
+
Matching bibstem, or None if not found.
|
|
119
|
+
"""
|
|
120
|
+
response = self._client.get(f"/journals/issn/{issn_number}")
|
|
121
|
+
return response.get("issn", {}).get("bibstem")
|
|
122
|
+
|
|
123
|
+
def browse(self, bibstem: str) -> dict:
|
|
124
|
+
"""
|
|
125
|
+
Get abbreviated summary for a journal.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
bibstem : str
|
|
130
|
+
Journal bibstem.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
dict
|
|
135
|
+
Abbreviated journal summary.
|
|
136
|
+
"""
|
|
137
|
+
response = self._client.get(f"/journals/browse/{bibstem}")
|
|
138
|
+
return response.get("browse", {})
|