crossref-local 0.3.1__py3-none-any.whl → 0.4.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.
- crossref_local/__init__.py +23 -9
- crossref_local/__main__.py +0 -0
- crossref_local/aio.py +0 -0
- crossref_local/api.py +104 -29
- crossref_local/cache.py +466 -0
- crossref_local/cache_export.py +83 -0
- crossref_local/cache_viz.py +296 -0
- crossref_local/citations.py +0 -0
- crossref_local/cli.py +205 -137
- crossref_local/cli_cache.py +179 -0
- crossref_local/cli_completion.py +245 -0
- crossref_local/cli_main.py +20 -0
- crossref_local/cli_mcp.py +275 -0
- crossref_local/config.py +21 -24
- crossref_local/db.py +0 -0
- crossref_local/fts.py +0 -0
- crossref_local/impact_factor/__init__.py +0 -0
- crossref_local/impact_factor/calculator.py +0 -0
- crossref_local/impact_factor/journal_lookup.py +0 -0
- crossref_local/mcp_server.py +262 -51
- crossref_local/models.py +0 -0
- crossref_local/remote.py +5 -0
- crossref_local/server.py +7 -7
- {crossref_local-0.3.1.dist-info → crossref_local-0.4.0.dist-info}/METADATA +63 -24
- crossref_local-0.4.0.dist-info/RECORD +27 -0
- {crossref_local-0.3.1.dist-info → crossref_local-0.4.0.dist-info}/entry_points.txt +1 -1
- crossref_local-0.3.1.dist-info/RECORD +0 -20
- {crossref_local-0.3.1.dist-info → crossref_local-0.4.0.dist-info}/WHEEL +0 -0
crossref_local/__init__.py
CHANGED
|
@@ -20,19 +20,19 @@ Async usage:
|
|
|
20
20
|
Configuration
|
|
21
21
|
-------------
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
DB mode (direct database access):
|
|
24
24
|
>>> from crossref_local import configure
|
|
25
25
|
>>> configure("/path/to/crossref.db")
|
|
26
26
|
Or set CROSSREF_LOCAL_DB environment variable.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
>>> from crossref_local import
|
|
30
|
-
>>>
|
|
31
|
-
Or set
|
|
28
|
+
HTTP mode (API access via HTTP):
|
|
29
|
+
>>> from crossref_local import configure_http
|
|
30
|
+
>>> configure_http("http://localhost:8333")
|
|
31
|
+
Or set CROSSREF_LOCAL_API_URL environment variable.
|
|
32
32
|
|
|
33
33
|
Typical setup with SSH tunnel:
|
|
34
|
-
$ ssh -L
|
|
35
|
-
>>>
|
|
34
|
+
$ ssh -L 8333:127.0.0.1:8333 your-server # In terminal
|
|
35
|
+
>>> configure_http() # Uses default localhost:8333
|
|
36
36
|
|
|
37
37
|
Public API
|
|
38
38
|
----------
|
|
@@ -43,6 +43,8 @@ Functions:
|
|
|
43
43
|
get(doi) -> Work | None
|
|
44
44
|
get_many(dois) -> list[Work]
|
|
45
45
|
exists(doi) -> bool
|
|
46
|
+
enrich(results) -> SearchResult
|
|
47
|
+
enrich_dois(dois) -> list[Work]
|
|
46
48
|
configure(db_path) -> None
|
|
47
49
|
configure_remote(api_url) -> None
|
|
48
50
|
get_mode() -> str
|
|
@@ -71,8 +73,11 @@ from .api import (
|
|
|
71
73
|
get,
|
|
72
74
|
get_many,
|
|
73
75
|
exists,
|
|
76
|
+
enrich,
|
|
77
|
+
enrich_dois,
|
|
74
78
|
configure,
|
|
75
|
-
|
|
79
|
+
configure_http,
|
|
80
|
+
configure_remote, # Backward compatibility alias
|
|
76
81
|
get_mode,
|
|
77
82
|
info,
|
|
78
83
|
)
|
|
@@ -86,6 +91,9 @@ from . import aio
|
|
|
86
91
|
# Citation network (public functions and classes)
|
|
87
92
|
from .citations import get_citing, get_cited, get_citation_count, CitationNetwork
|
|
88
93
|
|
|
94
|
+
# Cache module (public)
|
|
95
|
+
from . import cache
|
|
96
|
+
|
|
89
97
|
|
|
90
98
|
# Public API - what users should import
|
|
91
99
|
__all__ = [
|
|
@@ -97,9 +105,13 @@ __all__ = [
|
|
|
97
105
|
"get",
|
|
98
106
|
"get_many",
|
|
99
107
|
"exists",
|
|
108
|
+
# Enrichment (add citations/references to search results)
|
|
109
|
+
"enrich",
|
|
110
|
+
"enrich_dois",
|
|
100
111
|
# Configuration
|
|
101
112
|
"configure",
|
|
102
|
-
"
|
|
113
|
+
"configure_http",
|
|
114
|
+
"configure_remote", # Backward compatibility alias
|
|
103
115
|
"get_mode",
|
|
104
116
|
"info",
|
|
105
117
|
# Data models
|
|
@@ -107,6 +119,8 @@ __all__ = [
|
|
|
107
119
|
"SearchResult",
|
|
108
120
|
# Async API
|
|
109
121
|
"aio",
|
|
122
|
+
# Cache module
|
|
123
|
+
"cache",
|
|
110
124
|
# Citation network
|
|
111
125
|
"get_citing",
|
|
112
126
|
"get_cited",
|
crossref_local/__main__.py
CHANGED
|
File without changes
|
crossref_local/aio.py
CHANGED
|
File without changes
|
crossref_local/api.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""Main API for crossref_local.
|
|
2
2
|
|
|
3
3
|
Supports two modes:
|
|
4
|
-
-
|
|
5
|
-
-
|
|
4
|
+
- db: Direct database access (requires database file)
|
|
5
|
+
- http: HTTP API access (requires API server)
|
|
6
6
|
|
|
7
7
|
Mode is auto-detected or can be set explicitly via:
|
|
8
|
-
- CROSSREF_LOCAL_MODE environment variable ("
|
|
9
|
-
-
|
|
10
|
-
- configure() or
|
|
8
|
+
- CROSSREF_LOCAL_MODE environment variable ("db" or "http")
|
|
9
|
+
- CROSSREF_LOCAL_API_URL environment variable (API URL)
|
|
10
|
+
- configure() or configure_http() functions
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from typing import List, Optional
|
|
@@ -18,8 +18,8 @@ from .models import Work, SearchResult
|
|
|
18
18
|
from . import fts
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def
|
|
22
|
-
"""Get
|
|
21
|
+
def _get_http_client():
|
|
22
|
+
"""Get HTTP client (lazy import to avoid circular dependency)."""
|
|
23
23
|
from .remote import RemoteClient
|
|
24
24
|
|
|
25
25
|
return RemoteClient(Config.get_api_url())
|
|
@@ -48,8 +48,8 @@ def search(
|
|
|
48
48
|
>>> results = search("machine learning")
|
|
49
49
|
>>> print(f"Found {results.total} matches")
|
|
50
50
|
"""
|
|
51
|
-
if Config.get_mode() == "
|
|
52
|
-
client =
|
|
51
|
+
if Config.get_mode() == "http":
|
|
52
|
+
client = _get_http_client()
|
|
53
53
|
return client.search(query=query, limit=limit)
|
|
54
54
|
return fts.search(query, limit, offset)
|
|
55
55
|
|
|
@@ -64,8 +64,8 @@ def count(query: str) -> int:
|
|
|
64
64
|
Returns:
|
|
65
65
|
Number of matching works
|
|
66
66
|
"""
|
|
67
|
-
if Config.get_mode() == "
|
|
68
|
-
client =
|
|
67
|
+
if Config.get_mode() == "http":
|
|
68
|
+
client = _get_http_client()
|
|
69
69
|
result = client.search(query=query, limit=1)
|
|
70
70
|
return result.total
|
|
71
71
|
return fts.count(query)
|
|
@@ -86,8 +86,8 @@ def get(doi: str) -> Optional[Work]:
|
|
|
86
86
|
>>> work = get("10.1038/nature12373")
|
|
87
87
|
>>> print(work.title)
|
|
88
88
|
"""
|
|
89
|
-
if Config.get_mode() == "
|
|
90
|
-
client =
|
|
89
|
+
if Config.get_mode() == "http":
|
|
90
|
+
client = _get_http_client()
|
|
91
91
|
return client.get(doi)
|
|
92
92
|
db = get_db()
|
|
93
93
|
metadata = db.get_metadata(doi)
|
|
@@ -106,8 +106,8 @@ def get_many(dois: List[str]) -> List[Work]:
|
|
|
106
106
|
Returns:
|
|
107
107
|
List of Work objects (missing DOIs are skipped)
|
|
108
108
|
"""
|
|
109
|
-
if Config.get_mode() == "
|
|
110
|
-
client =
|
|
109
|
+
if Config.get_mode() == "http":
|
|
110
|
+
client = _get_http_client()
|
|
111
111
|
return client.get_many(dois)
|
|
112
112
|
db = get_db()
|
|
113
113
|
works = []
|
|
@@ -128,8 +128,8 @@ def exists(doi: str) -> bool:
|
|
|
128
128
|
Returns:
|
|
129
129
|
True if DOI exists
|
|
130
130
|
"""
|
|
131
|
-
if Config.get_mode() == "
|
|
132
|
-
client =
|
|
131
|
+
if Config.get_mode() == "http":
|
|
132
|
+
client = _get_http_client()
|
|
133
133
|
return client.exists(doi)
|
|
134
134
|
db = get_db()
|
|
135
135
|
row = db.fetchone("SELECT 1 FROM works WHERE doi = ?", (doi,))
|
|
@@ -151,29 +151,104 @@ def configure(db_path: str) -> None:
|
|
|
151
151
|
close_db() # Reset singleton to use new path
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
def
|
|
154
|
+
def configure_http(api_url: str = "http://localhost:8333") -> None:
|
|
155
155
|
"""
|
|
156
|
-
Configure for
|
|
156
|
+
Configure for HTTP API access.
|
|
157
157
|
|
|
158
158
|
Args:
|
|
159
159
|
api_url: URL of CrossRef Local API server
|
|
160
160
|
|
|
161
161
|
Example:
|
|
162
|
-
>>> from crossref_local import
|
|
163
|
-
>>>
|
|
162
|
+
>>> from crossref_local import configure_http
|
|
163
|
+
>>> configure_http("http://localhost:8333")
|
|
164
164
|
>>> # Or via SSH tunnel:
|
|
165
|
-
>>> # ssh -L
|
|
166
|
-
>>>
|
|
165
|
+
>>> # ssh -L 8333:127.0.0.1:8333 your-server
|
|
166
|
+
>>> configure_http() # Uses default localhost:8333
|
|
167
167
|
"""
|
|
168
168
|
Config.set_api_url(api_url)
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
# Backward compatibility alias
|
|
172
|
+
configure_remote = configure_http
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def enrich(
|
|
176
|
+
results: SearchResult,
|
|
177
|
+
include_citations: bool = True,
|
|
178
|
+
include_references: bool = True,
|
|
179
|
+
) -> SearchResult:
|
|
180
|
+
"""
|
|
181
|
+
Enrich search results with full metadata (citations, references).
|
|
182
|
+
|
|
183
|
+
The search() function returns basic metadata for speed. This function
|
|
184
|
+
fetches full metadata for each work, adding citation counts and references.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
results: SearchResult from search()
|
|
188
|
+
include_citations: Include citation counts
|
|
189
|
+
include_references: Include reference DOIs
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
SearchResult with enriched works
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
>>> from crossref_local import search, enrich
|
|
196
|
+
>>> results = search("machine learning", limit=10)
|
|
197
|
+
>>> enriched = enrich(results)
|
|
198
|
+
>>> for work in enriched:
|
|
199
|
+
... print(f"{work.title}: {work.citation_count} citations")
|
|
200
|
+
"""
|
|
201
|
+
enriched_works = []
|
|
202
|
+
for work in results.works:
|
|
203
|
+
full_work = get(work.doi)
|
|
204
|
+
if full_work:
|
|
205
|
+
enriched_works.append(full_work)
|
|
206
|
+
else:
|
|
207
|
+
# Keep original if full metadata not available
|
|
208
|
+
enriched_works.append(work)
|
|
209
|
+
|
|
210
|
+
return SearchResult(
|
|
211
|
+
works=enriched_works,
|
|
212
|
+
total=results.total,
|
|
213
|
+
query=results.query,
|
|
214
|
+
elapsed_ms=results.elapsed_ms,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def enrich_dois(
|
|
219
|
+
dois: List[str],
|
|
220
|
+
include_citations: bool = True,
|
|
221
|
+
include_references: bool = True,
|
|
222
|
+
) -> List[Work]:
|
|
223
|
+
"""
|
|
224
|
+
Enrich a list of DOIs with full metadata.
|
|
225
|
+
|
|
226
|
+
Fetches complete metadata for each DOI including citation counts
|
|
227
|
+
and reference lists.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
dois: List of DOIs to enrich
|
|
231
|
+
include_citations: Include citation counts
|
|
232
|
+
include_references: Include reference DOIs
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
List of Work objects with full metadata
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
>>> from crossref_local import enrich_dois
|
|
239
|
+
>>> works = enrich_dois(["10.1038/nature12373", "10.1126/science.aax0758"])
|
|
240
|
+
>>> for w in works:
|
|
241
|
+
... print(f"{w.doi}: {w.citation_count} citations, {len(w.references)} refs")
|
|
242
|
+
"""
|
|
243
|
+
return get_many(dois)
|
|
244
|
+
|
|
245
|
+
|
|
171
246
|
def get_mode() -> str:
|
|
172
247
|
"""
|
|
173
248
|
Get current mode.
|
|
174
249
|
|
|
175
250
|
Returns:
|
|
176
|
-
"
|
|
251
|
+
"db" or "http"
|
|
177
252
|
"""
|
|
178
253
|
return Config.get_mode()
|
|
179
254
|
|
|
@@ -187,10 +262,10 @@ def info() -> dict:
|
|
|
187
262
|
"""
|
|
188
263
|
mode = Config.get_mode()
|
|
189
264
|
|
|
190
|
-
if mode == "
|
|
191
|
-
client =
|
|
192
|
-
|
|
193
|
-
return {"mode": "
|
|
265
|
+
if mode == "http":
|
|
266
|
+
client = _get_http_client()
|
|
267
|
+
http_info = client.info()
|
|
268
|
+
return {"mode": "http", **http_info}
|
|
194
269
|
|
|
195
270
|
db = get_db()
|
|
196
271
|
|
|
@@ -213,7 +288,7 @@ def info() -> dict:
|
|
|
213
288
|
citation_count = 0
|
|
214
289
|
|
|
215
290
|
return {
|
|
216
|
-
"mode": "
|
|
291
|
+
"mode": "db",
|
|
217
292
|
"db_path": str(Config.get_db_path()),
|
|
218
293
|
"works": work_count,
|
|
219
294
|
"fts_indexed": fts_count,
|