crossref-local 0.3.0__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.
@@ -0,0 +1,352 @@
1
+ """FastAPI server for CrossRef Local with FTS5 search.
2
+
3
+ This server provides proper full-text search using FTS5 index,
4
+ unlike the Django API which only scans a limited subset.
5
+
6
+ Usage:
7
+ crossref-local api # Run on default port 8333
8
+ crossref-local api --port 8080 # Custom port
9
+
10
+ # Or directly:
11
+ uvicorn crossref_local.server:app --host 0.0.0.0 --port 8333
12
+ """
13
+
14
+ import time
15
+ from typing import Optional, List
16
+
17
+ from fastapi import FastAPI, Query, HTTPException
18
+ from fastapi.middleware.cors import CORSMiddleware
19
+ from pydantic import BaseModel
20
+
21
+ from . import fts, __version__
22
+ from .db import get_db
23
+ from .models import Work
24
+
25
+ app = FastAPI(
26
+ title="CrossRef Local API",
27
+ description="Fast full-text search across 167M+ scholarly works",
28
+ version=__version__,
29
+ )
30
+
31
+ # CORS middleware
32
+ app.add_middleware(
33
+ CORSMiddleware,
34
+ allow_origins=["*"],
35
+ allow_methods=["*"],
36
+ allow_headers=["*"],
37
+ )
38
+
39
+
40
+ class WorkResponse(BaseModel):
41
+ doi: str
42
+ title: Optional[str] = None
43
+ authors: List[str] = []
44
+ year: Optional[int] = None
45
+ journal: Optional[str] = None
46
+ issn: Optional[str] = None
47
+ volume: Optional[str] = None
48
+ issue: Optional[str] = None
49
+ page: Optional[str] = None
50
+ abstract: Optional[str] = None
51
+ citation_count: Optional[int] = None
52
+
53
+
54
+ class SearchResponse(BaseModel):
55
+ query: str
56
+ total: int
57
+ returned: int
58
+ elapsed_ms: float
59
+ results: List[WorkResponse]
60
+
61
+
62
+ class InfoResponse(BaseModel):
63
+ name: str = "CrossRef Local API"
64
+ version: str = __version__
65
+ status: str = "running"
66
+ mode: str = "local"
67
+ total_papers: int
68
+ fts_indexed: int
69
+ citations: int
70
+ database_path: str
71
+
72
+
73
+ @app.get("/")
74
+ def root():
75
+ """API root with endpoint information."""
76
+ return {
77
+ "name": "CrossRef Local API",
78
+ "version": __version__,
79
+ "status": "running",
80
+ "endpoints": {
81
+ "health": "/health",
82
+ "info": "/info",
83
+ "search": "/works?q=<query>",
84
+ "get_by_doi": "/works/{doi}",
85
+ "batch": "/works/batch",
86
+ },
87
+ }
88
+
89
+
90
+ @app.get("/health")
91
+ def health():
92
+ """Health check endpoint."""
93
+ db = get_db()
94
+ return {
95
+ "status": "healthy",
96
+ "database_connected": db is not None,
97
+ "database_path": str(db.db_path) if db else None,
98
+ }
99
+
100
+
101
+ @app.get("/info", response_model=InfoResponse)
102
+ def info():
103
+ """Get database statistics."""
104
+ db = get_db()
105
+
106
+ row = db.fetchone("SELECT COUNT(*) as count FROM works")
107
+ work_count = row["count"] if row else 0
108
+
109
+ try:
110
+ row = db.fetchone("SELECT COUNT(*) as count FROM works_fts")
111
+ fts_count = row["count"] if row else 0
112
+ except Exception:
113
+ fts_count = 0
114
+
115
+ try:
116
+ row = db.fetchone("SELECT COUNT(*) as count FROM citations")
117
+ citation_count = row["count"] if row else 0
118
+ except Exception:
119
+ citation_count = 0
120
+
121
+ return InfoResponse(
122
+ total_papers=work_count,
123
+ fts_indexed=fts_count,
124
+ citations=citation_count,
125
+ database_path=str(db.db_path),
126
+ )
127
+
128
+
129
+ @app.get("/works", response_model=SearchResponse)
130
+ def search_works(
131
+ q: str = Query(..., description="Search query (FTS5 syntax supported)"),
132
+ limit: int = Query(10, ge=1, le=100, description="Max results"),
133
+ offset: int = Query(0, ge=0, description="Skip first N results"),
134
+ ):
135
+ """
136
+ Full-text search across works.
137
+
138
+ Uses FTS5 index for fast searching across titles, abstracts, and authors.
139
+ Supports FTS5 query syntax like AND, OR, NOT, "exact phrases".
140
+
141
+ Examples:
142
+ /works?q=machine learning
143
+ /works?q="neural network" AND hippocampus
144
+ /works?q=CRISPR&limit=20
145
+ """
146
+ start = time.perf_counter()
147
+
148
+ try:
149
+ results = fts.search(q, limit=limit, offset=offset)
150
+ except Exception as e:
151
+ raise HTTPException(status_code=400, detail=f"Search error: {e}")
152
+
153
+ elapsed_ms = (time.perf_counter() - start) * 1000
154
+
155
+ return SearchResponse(
156
+ query=q,
157
+ total=results.total,
158
+ returned=len(results.works),
159
+ elapsed_ms=round(elapsed_ms, 2),
160
+ results=[
161
+ WorkResponse(
162
+ doi=w.doi,
163
+ title=w.title,
164
+ authors=w.authors,
165
+ year=w.year,
166
+ journal=w.journal,
167
+ issn=w.issn,
168
+ volume=w.volume,
169
+ issue=w.issue,
170
+ page=w.page,
171
+ abstract=w.abstract,
172
+ citation_count=w.citation_count,
173
+ )
174
+ for w in results.works
175
+ ],
176
+ )
177
+
178
+
179
+ @app.get("/works/{doi:path}", response_model=Optional[WorkResponse])
180
+ def get_work(doi: str):
181
+ """
182
+ Get work metadata by DOI.
183
+
184
+ Examples:
185
+ /works/10.1038/nature12373
186
+ /works/10.1016/j.cell.2020.01.001
187
+ """
188
+ db = get_db()
189
+ metadata = db.get_metadata(doi)
190
+
191
+ if metadata is None:
192
+ raise HTTPException(status_code=404, detail=f"DOI not found: {doi}")
193
+
194
+ work = Work.from_metadata(doi, metadata)
195
+
196
+ return WorkResponse(
197
+ doi=work.doi,
198
+ title=work.title,
199
+ authors=work.authors,
200
+ year=work.year,
201
+ journal=work.journal,
202
+ issn=work.issn,
203
+ volume=work.volume,
204
+ issue=work.issue,
205
+ page=work.page,
206
+ abstract=work.abstract,
207
+ citation_count=work.citation_count,
208
+ )
209
+
210
+
211
+ class BatchRequest(BaseModel):
212
+ dois: List[str]
213
+
214
+
215
+ class BatchResponse(BaseModel):
216
+ requested: int
217
+ found: int
218
+ results: List[WorkResponse]
219
+
220
+
221
+ @app.post("/works/batch", response_model=BatchResponse)
222
+ def get_works_batch(request: BatchRequest):
223
+ """
224
+ Get multiple works by DOI.
225
+
226
+ Request body: {"dois": ["10.1038/...", "10.1016/..."]}
227
+ """
228
+ db = get_db()
229
+ results = []
230
+
231
+ for doi in request.dois:
232
+ metadata = db.get_metadata(doi)
233
+ if metadata:
234
+ work = Work.from_metadata(doi, metadata)
235
+ results.append(
236
+ WorkResponse(
237
+ doi=work.doi,
238
+ title=work.title,
239
+ authors=work.authors,
240
+ year=work.year,
241
+ journal=work.journal,
242
+ abstract=work.abstract,
243
+ citation_count=work.citation_count,
244
+ )
245
+ )
246
+
247
+ return BatchResponse(
248
+ requested=len(request.dois),
249
+ found=len(results),
250
+ results=results,
251
+ )
252
+
253
+
254
+ # For backwards compatibility with existing API endpoints
255
+ @app.get("/api/search/")
256
+ def api_search_compat(
257
+ title: Optional[str] = None,
258
+ q: Optional[str] = None,
259
+ doi: Optional[str] = None,
260
+ limit: int = 10,
261
+ ):
262
+ """Backwards-compatible search endpoint."""
263
+ query = title or q
264
+
265
+ if doi:
266
+ # DOI lookup
267
+ try:
268
+ work = get_work(doi)
269
+ return {
270
+ "query": {"doi": doi},
271
+ "results": [work.model_dump()],
272
+ "total": 1,
273
+ "returned": 1,
274
+ }
275
+ except HTTPException:
276
+ return {"query": {"doi": doi}, "results": [], "total": 0, "returned": 0}
277
+
278
+ if not query:
279
+ raise HTTPException(
280
+ status_code=400, detail="Specify q, title, or doi parameter"
281
+ )
282
+
283
+ # Call fts.search directly (not the endpoint function)
284
+ results = fts.search(query, limit=limit, offset=0)
285
+ return {
286
+ "query": {
287
+ "title": query,
288
+ "doi": None,
289
+ "year": None,
290
+ "authors": None,
291
+ "limit": limit,
292
+ },
293
+ "results": [
294
+ WorkResponse(
295
+ doi=w.doi,
296
+ title=w.title,
297
+ authors=w.authors,
298
+ year=w.year,
299
+ journal=w.journal,
300
+ issn=w.issn,
301
+ volume=w.volume,
302
+ issue=w.issue,
303
+ page=w.page,
304
+ abstract=w.abstract,
305
+ citation_count=w.citation_count,
306
+ ).model_dump()
307
+ for w in results.works
308
+ ],
309
+ "total": results.total,
310
+ "returned": len(results.works),
311
+ }
312
+
313
+
314
+ @app.get("/api/stats/")
315
+ def api_stats_compat():
316
+ """Backwards-compatible stats endpoint."""
317
+ db = get_db()
318
+
319
+ row = db.fetchone("SELECT COUNT(*) as count FROM works")
320
+ work_count = row["count"] if row else 0
321
+
322
+ # Get table names
323
+ tables = []
324
+ for row in db.fetchall("SELECT name FROM sqlite_master WHERE type='table'"):
325
+ tables.append(row["name"])
326
+
327
+ # Get index names
328
+ indices = []
329
+ for row in db.fetchall("SELECT name FROM sqlite_master WHERE type='index'"):
330
+ if row["name"]:
331
+ indices.append(row["name"])
332
+
333
+ return {
334
+ "total_papers": work_count,
335
+ "database_size_mb": None,
336
+ "year_range": None,
337
+ "total_journals": 0,
338
+ "total_citations": None,
339
+ "tables": tables,
340
+ "indices": indices,
341
+ }
342
+
343
+
344
+ def run_server(host: str = "0.0.0.0", port: int = 8333):
345
+ """Run the FastAPI server."""
346
+ import uvicorn
347
+
348
+ uvicorn.run(app, host=host, port=port)
349
+
350
+
351
+ if __name__ == "__main__":
352
+ run_server()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crossref-local
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Local CrossRef database with 167M+ works and full-text search
5
5
  Project-URL: Homepage, https://github.com/ywatanabe1989/crossref_local
6
6
  Project-URL: Repository, https://github.com/ywatanabe1989/crossref_local
@@ -18,15 +18,37 @@ Classifier: Topic :: Database
18
18
  Classifier: Topic :: Scientific/Engineering
19
19
  Requires-Python: >=3.10
20
20
  Requires-Dist: click>=8.0
21
+ Provides-Extra: all
22
+ Requires-Dist: fastapi>=0.100; extra == 'all'
23
+ Requires-Dist: fastmcp>=0.4; extra == 'all'
24
+ Requires-Dist: matplotlib>=3.7; extra == 'all'
25
+ Requires-Dist: networkx>=3.0; extra == 'all'
26
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'all'
27
+ Requires-Dist: pytest-cov>=4.0; extra == 'all'
28
+ Requires-Dist: pytest>=7.0; extra == 'all'
29
+ Requires-Dist: pyvis>=0.3; extra == 'all'
30
+ Requires-Dist: uvicorn>=0.20; extra == 'all'
31
+ Provides-Extra: api
32
+ Requires-Dist: fastapi>=0.100; extra == 'api'
33
+ Requires-Dist: uvicorn>=0.20; extra == 'api'
21
34
  Provides-Extra: dev
35
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
22
36
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
23
37
  Requires-Dist: pytest>=7.0; extra == 'dev'
38
+ Provides-Extra: mcp
39
+ Requires-Dist: fastmcp>=0.4; extra == 'mcp'
24
40
  Provides-Extra: viz
25
41
  Requires-Dist: matplotlib>=3.7; extra == 'viz'
26
42
  Requires-Dist: networkx>=3.0; extra == 'viz'
27
43
  Requires-Dist: pyvis>=0.3; extra == 'viz'
28
44
  Description-Content-Type: text/markdown
29
45
 
46
+ <!-- ---
47
+ !-- Timestamp: 2026-01-16 19:15:51
48
+ !-- Author: ywatanabe
49
+ !-- File: /home/ywatanabe/proj/crossref-local/README.md
50
+ !-- --- -->
51
+
30
52
  # CrossRef Local
31
53
 
32
54
  Local CrossRef database with 167M+ scholarly works, full-text search, and impact factor calculation.
@@ -39,6 +61,22 @@ Local CrossRef database with 167M+ scholarly works, full-text search, and impact
39
61
  <img src="examples/readme_figure.png" alt="CrossRef Local Demo" width="800"/>
40
62
  </p>
41
63
 
64
+ <details>
65
+ <summary><strong>MCP Demo Video</strong></summary>
66
+
67
+ <p align="center">
68
+ <a href="https://scitex.ai/media/videos/crossref-local-v0.3.1-demo.mp4">
69
+ <img src="examples/demo_mcp_out/crossref-local-v0.3.1-demo-thumbnail_6m55s.png" alt="Demo Video Thumbnail" width="600"/>
70
+ </a>
71
+ </p>
72
+
73
+ Live demonstration of MCP server integration with Claude Code for `epilepsy seizure prediction` literature review:
74
+ - Full-text search on title, abstracts, and keywords across 167M papers (22ms response)
75
+
76
+ 📄 [Full demo documentation](examples/demo_mcp.org) | 📊 [Generated diagrams](examples/demo_mcp_out/)
77
+
78
+ </details>
79
+
42
80
  <details>
43
81
  <summary><strong>Why CrossRef Local?</strong></summary>
44
82
 
@@ -119,24 +157,110 @@ async def main():
119
157
 
120
158
  ```bash
121
159
  crossref-local search "CRISPR genome editing" -n 5
122
- crossref-local get 10.1038/nature12373
123
- crossref-local impact-factor Nature -y 2023 # IF: 54.067
160
+ crossref-local search-by-doi 10.1038/nature12373
161
+ crossref-local status # Configuration and database stats
124
162
  ```
125
163
 
126
164
  With abstracts (`-a` flag):
127
165
  ```
128
- $ crossref-local search "CRISPR" -n 1 -a
166
+ $ crossref-local search "RS-1 enhances CRISPR" -n 1 -a
129
167
 
130
- Found 87,473 matches in 18.2ms
168
+ Found 4 matches in 128.4ms
131
169
 
132
170
  1. RS-1 enhances CRISPR/Cas9- and TALEN-mediated knock-in efficiency (2016)
133
171
  DOI: 10.1038/ncomms10548
134
172
  Journal: Nature Communications
135
173
  Abstract: Zinc-finger nuclease, transcription activator-like effector nuclease
136
- and CRISPR/Cas9 are becoming major tools for genome editing. Importantly,
137
- knock-in in several non-rodent species has been finally achieved...
174
+ and CRISPR/Cas9 are becoming major tools for genome editing...
175
+ ```
176
+
177
+ </details>
178
+
179
+ <details>
180
+ <summary><strong>HTTP API</strong></summary>
181
+
182
+ Start the FastAPI server:
183
+ ```bash
184
+ crossref-local run-server-http --host 0.0.0.0 --port 8333
185
+ ```
186
+
187
+ Endpoints:
188
+ ```bash
189
+ # Search works (FTS5)
190
+ curl "http://localhost:8333/works?q=CRISPR&limit=10"
191
+
192
+ # Get by DOI
193
+ curl "http://localhost:8333/works/10.1038/nature12373"
194
+
195
+ # Batch DOI lookup
196
+ curl -X POST "http://localhost:8333/works/batch" \
197
+ -H "Content-Type: application/json" \
198
+ -d '{"dois": ["10.1038/nature12373", "10.1126/science.aax0758"]}'
199
+
200
+ # Database info
201
+ curl "http://localhost:8333/info"
138
202
  ```
139
203
 
204
+ HTTP mode (connect to running server):
205
+ ```bash
206
+ # On local machine (if server is remote)
207
+ ssh -L 8333:127.0.0.1:8333 your-server
208
+
209
+ # Python client
210
+ from crossref_local import configure_http
211
+ configure_http("http://localhost:8333")
212
+
213
+ # Or via CLI
214
+ crossref-local --http search "CRISPR"
215
+ ```
216
+
217
+ </details>
218
+
219
+ <details>
220
+ <summary><strong>MCP Server</strong></summary>
221
+
222
+ Run as MCP (Model Context Protocol) server:
223
+ ```bash
224
+ crossref-local run-server-mcp
225
+ ```
226
+
227
+ Local MCP client configuration:
228
+ ```json
229
+ {
230
+ "mcpServers": {
231
+ "crossref-local": {
232
+ "command": "crossref-local",
233
+ "args": ["run-server-mcp"],
234
+ "env": {
235
+ "CROSSREF_LOCAL_DB": "/path/to/crossref.db"
236
+ }
237
+ }
238
+ }
239
+ }
240
+ ```
241
+
242
+ Remote MCP via HTTP (recommended):
243
+ ```bash
244
+ # On server: start persistent MCP server
245
+ crossref-local run-server-mcp -t http --host 0.0.0.0 --port 8082
246
+ ```
247
+ ```json
248
+ {
249
+ "mcpServers": {
250
+ "crossref-remote": {
251
+ "url": "http://your-server:8082/mcp"
252
+ }
253
+ }
254
+ }
255
+ ```
256
+
257
+ See [docs/remote-deployment.md](docs/remote-deployment.md) for systemd and Docker setup.
258
+
259
+ Available tools:
260
+ - `search` - Full-text search across 167M+ papers
261
+ - `search_by_doi` - Get paper by DOI
262
+ - `status` - Database statistics
263
+
140
264
  </details>
141
265
 
142
266
  <details>
@@ -188,6 +312,27 @@ Searching 167M records in milliseconds via FTS5.
188
312
 
189
313
  </details>
190
314
 
315
+ <details>
316
+ <summary><strong>Related Projects</strong></summary>
317
+
318
+ **[openalex-local](https://github.com/ywatanabe1989/openalex-local)** - Sister project with OpenAlex data:
319
+
320
+ | Feature | crossref-local | openalex-local |
321
+ |---------|----------------|----------------|
322
+ | Works | 167M | 284M |
323
+ | Abstracts | ~21% | ~45-60% |
324
+ | Update frequency | Real-time | Monthly |
325
+ | DOI authority | ✓ (source) | Uses CrossRef |
326
+ | Citations | Raw references | Linked works |
327
+ | Concepts/Topics | ❌ | ✓ |
328
+ | Author IDs | ❌ | ✓ |
329
+ | Best for | DOI lookup, raw refs | Semantic search |
330
+
331
+ **When to use CrossRef**: Real-time DOI updates, raw reference parsing, authoritative metadata.
332
+ **When to use OpenAlex**: Semantic search, citation analysis, topic discovery.
333
+
334
+ </details>
335
+
191
336
 
192
337
  ---
193
338
 
@@ -0,0 +1,27 @@
1
+ crossref_local/__init__.py,sha256=eCvA9q-nZSWCIoMZxINV5xsboXovv4YeqZfjfLzi820,3696
2
+ crossref_local/__main__.py,sha256=N1c1ESGgJkAwsSWXANUgmzxC1OJEIqw-cl9m4pmNP7s,110
3
+ crossref_local/aio.py,sha256=En2btSn3euRbEYav1919gsmdC8iQaMbgGUso-IThCwo,5490
4
+ crossref_local/api.py,sha256=4VGThHU3RZaH5dgjax69iUHfRlh7j91LaM00guDKtKs,7436
5
+ crossref_local/cache.py,sha256=sMrmiUJr8-zRBDXnHg0M_3q3MpgzT7V1bqMKb5kpKVg,12291
6
+ crossref_local/cache_export.py,sha256=DU44Rv894fZz6OnBETbgwYzwVOPg3zJ6sP85yE5EYbc,2519
7
+ crossref_local/cache_viz.py,sha256=0VAHK-i1xR5StEYrJ_IAs0HSk0MQNVFi4EjPtlZEwTY,8498
8
+ crossref_local/citations.py,sha256=QFahv84upNnXP_89A8bHxEbAdz7wHbh5LEniGcAiHas,12402
9
+ crossref_local/cli.py,sha256=OMgrWviTT6Q9wLPWyhb9qDiRlVAJCuRDwDVpddyRPUk,16352
10
+ crossref_local/cli_cache.py,sha256=8QykJ7-H313x7oKRw5fJ9Coay27Y8uBWGx5JR1VQ1_Y,6220
11
+ crossref_local/cli_completion.py,sha256=yzqwMbGbbqXJQHdztuaqji-AdFTd8zDDhHeNGdw4NpU,7341
12
+ crossref_local/cli_main.py,sha256=NgCGB5-ThofTRPoxxwZpsfCQTo5WIw8oP7zBSKhEiDQ,444
13
+ crossref_local/cli_mcp.py,sha256=JBGveR_K7M_EzmHb3G9xr1IZPHAf-vvht-yAn5IzkQM,8524
14
+ crossref_local/config.py,sha256=tBAqnnscLOIF5dobEjOA4b57OVfGeGhpnmRonYKt7ng,4839
15
+ crossref_local/db.py,sha256=x7dXQXjsFN4LavtkNAKTNw1cUBMG-2h53-Z-Xlq6aoQ,3696
16
+ crossref_local/fts.py,sha256=yZMh_vmtFentXKAFGTS4z7ZNNj7p_ItgfFP5i0yQltw,4448
17
+ crossref_local/mcp_server.py,sha256=BN1BByTI54Rh-QoMZA2wUTV7lxdX-fLvoiEaH3YP-W0,10974
18
+ crossref_local/models.py,sha256=b_yYb91O6RwEPpEqe2Wmdz12WIfE5itjEus4-fCLxLI,5476
19
+ crossref_local/remote.py,sha256=Ju4NewXsw_cNX4vq6OeSu8fKtyf9CLwno-4uWkpwAOM,8638
20
+ crossref_local/server.py,sha256=CpCZm5ZVLSPfcke1lGkvEOp1XsH6bUGm5M0i9pj343U,9059
21
+ crossref_local/impact_factor/__init__.py,sha256=pcgVCPogBisANYE5Vp2PHVGPgxoMsSXr-6utqVE97-4,559
22
+ crossref_local/impact_factor/calculator.py,sha256=eZ13URAZzPdRyAQpS8zXe_T33e2lm_gQhtoJCXbfIGM,15977
23
+ crossref_local/impact_factor/journal_lookup.py,sha256=Ztx6ZeWxfmPvA3KfcW5h_yz01XPstIdk91j3nu2Q-qw,8846
24
+ crossref_local-0.4.0.dist-info/METADATA,sha256=l-quaEeSKQwlveyZgKwZfW1SwyNsM9G8yZhGOHtGtMs,9508
25
+ crossref_local-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
26
+ crossref_local-0.4.0.dist-info/entry_points.txt,sha256=1LhHFzJ7VWhNmGY6yIGDJ5EhKTYCyU7CbLEooXQWhLQ,116
27
+ crossref_local-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ crossref-local = crossref_local.cli_main:main
3
+ crossref-local-mcp = crossref_local.mcp_server:main
@@ -1,16 +0,0 @@
1
- crossref_local/__init__.py,sha256=vJFOk5somcdkuTT7UhVjAR4kj8p5JYVVHgmgCtTmENs,1544
2
- crossref_local/aio.py,sha256=En2btSn3euRbEYav1919gsmdC8iQaMbgGUso-IThCwo,5490
3
- crossref_local/api.py,sha256=FPZBStNLD7hnjjnpUzKfBFKo7gv3JHOLEIFSydH53bw,3370
4
- crossref_local/citations.py,sha256=QFahv84upNnXP_89A8bHxEbAdz7wHbh5LEniGcAiHas,12402
5
- crossref_local/cli.py,sha256=9ISMQSvWZHYrEO_gYXJB-Ju0lZCr9ifH8jFnltkgYTU,8639
6
- crossref_local/config.py,sha256=_tHZOHBbbw5BcmkWdTlqbSffijlQZkPkexl2YxW4GmE,1980
7
- crossref_local/db.py,sha256=xiTYFQxsIXmV6_QmXMWf6eX7GJv5D4icy67lBvQjUQI,3529
8
- crossref_local/fts.py,sha256=e5mxAbHWrO9E1GvG1WFDFxoCs_4RQIdAPYLSuMf8JCM,3395
9
- crossref_local/models.py,sha256=b_yYb91O6RwEPpEqe2Wmdz12WIfE5itjEus4-fCLxLI,5476
10
- crossref_local/impact_factor/__init__.py,sha256=pcgVCPogBisANYE5Vp2PHVGPgxoMsSXr-6utqVE97-4,559
11
- crossref_local/impact_factor/calculator.py,sha256=eZ13URAZzPdRyAQpS8zXe_T33e2lm_gQhtoJCXbfIGM,15977
12
- crossref_local/impact_factor/journal_lookup.py,sha256=Ztx6ZeWxfmPvA3KfcW5h_yz01XPstIdk91j3nu2Q-qw,8846
13
- crossref_local-0.3.0.dist-info/METADATA,sha256=3z5SlpYph6S6V6fwu5omXXyyNeGubZGvyqic8BU8-n8,5659
14
- crossref_local-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
15
- crossref_local-0.3.0.dist-info/entry_points.txt,sha256=TaFQ1y-tIym2dqgE6xUUeXTvy2uCHNKoYeRO4w6ndWQ,59
16
- crossref_local-0.3.0.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- crossref-local = crossref_local.cli:main