crossref-local 0.5.0__py3-none-any.whl → 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.
@@ -1,23 +1,49 @@
1
1
  """Work search and retrieval endpoints."""
2
2
 
3
3
  import time
4
- from typing import Optional
4
+ from typing import Optional, Dict
5
5
 
6
6
  from fastapi import APIRouter, Query, HTTPException
7
7
 
8
8
  from .._core import fts
9
9
  from .._core.db import get_db
10
10
  from .._core.models import Work
11
- from .models import WorkResponse, SearchResponse, BatchRequest, BatchResponse
11
+ from .models import (
12
+ BatchRequest,
13
+ BatchResponse,
14
+ SearchResponse,
15
+ WorkResponse,
16
+ )
12
17
 
13
18
  router = APIRouter(tags=["works"])
14
19
 
20
+ # IF cache for performance
21
+ _if_cache: Dict[str, Optional[float]] = {}
22
+
23
+
24
+ def _get_impact_factor(db, issn: str) -> Optional[float]:
25
+ """Get impact factor from journals_openalex table."""
26
+ if not issn:
27
+ return None
28
+ if issn in _if_cache:
29
+ return _if_cache[issn]
30
+ try:
31
+ row = db.fetchone(
32
+ "SELECT two_year_mean_citedness FROM journals_openalex WHERE issns LIKE ?",
33
+ (f"%{issn}%",),
34
+ )
35
+ _if_cache[issn] = row["two_year_mean_citedness"] if row else None
36
+ except Exception:
37
+ _if_cache[issn] = None
38
+ return _if_cache[issn]
39
+
15
40
 
16
41
  @router.get("/works", response_model=SearchResponse)
17
42
  def search_works(
18
43
  q: str = Query(..., description="Search query (FTS5 syntax supported)"),
19
- limit: int = Query(10, ge=1, le=100, description="Max results"),
44
+ limit: int = Query(10, ge=1, description="Max results"),
20
45
  offset: int = Query(0, ge=0, description="Skip first N results"),
46
+ with_if: bool = Query(False, description="Include impact factor (OpenAlex)"),
21
47
  ):
22
48
  """
23
49
  Full-text search across works.
@@ -28,7 +54,7 @@ def search_works(
28
54
  Examples:
29
55
  /works?q=machine learning
30
56
  /works?q="neural network" AND hippocampus
31
- /works?q=CRISPR&limit=20
57
+ /works?q=CRISPR&limit=20&with_if=true
32
58
  """
33
59
  start = time.perf_counter()
34
60
 
@@ -37,14 +63,13 @@ def search_works(
37
63
  except Exception as e:
38
64
  raise HTTPException(status_code=400, detail=f"Search error: {e}")
39
65
 
40
- elapsed_ms = (time.perf_counter() - start) * 1000
66
+ # Get IF if requested
67
+ db = get_db() if with_if else None
41
68
 
42
- return SearchResponse(
43
- query=q,
44
- total=results.total,
45
- returned=len(results.works),
46
- elapsed_ms=round(elapsed_ms, 2),
47
- results=[
69
+ work_responses = []
70
+ for w in results.works:
71
+ if_val = _get_impact_factor(db, w.issn) if db and w.issn else None
72
+ work_responses.append(
48
73
  WorkResponse(
49
74
  doi=w.doi,
50
75
  title=w.title,
@@ -57,9 +82,34 @@ def search_works(
57
82
  page=w.page,
58
83
  abstract=w.abstract,
59
84
  citation_count=w.citation_count,
85
+ impact_factor=if_val,
86
+ impact_factor_source="OpenAlex" if if_val else None,
60
87
  )
61
- for w in results.works
62
- ],
88
+ )
89
+
90
+ elapsed_ms = (time.perf_counter() - start) * 1000
91
+
92
+ # Build limit_info from search result
93
+ limit_info = None
94
+ if results.limit_info:
95
+ from .models import LimitInfoResponse
96
+
97
+ limit_info = LimitInfoResponse(
98
+ requested=results.limit_info.requested,
99
+ returned=results.limit_info.returned,
100
+ total_available=results.limit_info.total_available,
101
+ capped=results.limit_info.capped,
102
+ capped_reason=results.limit_info.capped_reason,
103
+ stage=results.limit_info.stage,
104
+ )
105
+
106
+ return SearchResponse(
107
+ query=q,
108
+ total=results.total,
109
+ returned=len(results.works),
110
+ elapsed_ms=round(elapsed_ms, 2),
111
+ results=work_responses,
112
+ limit_info=limit_info,
63
113
  )
64
114
 
65
115
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crossref-local
3
- Version: 0.5.0
3
+ Version: 0.5.1
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
@@ -1,4 +1,4 @@
1
- crossref_local/__init__.py,sha256=bOn6qJob4okwkLzzIoEGQj0I-amIBSr89iWrbkYEJns,3691
1
+ crossref_local/__init__.py,sha256=j2HyOmjYFUjiywoLdIgYVqgXuGx9eWjcy4x7VBA4vf0,3792
2
2
  crossref_local/__main__.py,sha256=N1c1ESGgJkAwsSWXANUgmzxC1OJEIqw-cl9m4pmNP7s,110
3
3
  crossref_local/aio.py,sha256=_vDHFoE2n6GqO3j6pGeCaJVVe5I5L3M0tuMlW8IgGx8,1255
4
4
  crossref_local/cache.py,sha256=yQpnRRBsHx2pluIPPfJlrakeuCC8TysKXrfA3ohdxSg,13321
@@ -15,33 +15,35 @@ crossref_local/_cache/utils.py,sha256=D3sg7MoXZs4aC2OvagVgmOVMZjm1Xqy1ulGbs0_F91
15
15
  crossref_local/_cache/viz.py,sha256=0VAHK-i1xR5StEYrJ_IAs0HSk0MQNVFi4EjPtlZEwTY,8498
16
16
  crossref_local/_cli/__init__.py,sha256=NS07Eo93dRAuO5vxGwGwS0ctvZxMMkF_py09bzIk3Hk,175
17
17
  crossref_local/_cli/cache.py,sha256=8QykJ7-H313x7oKRw5fJ9Coay27Y8uBWGx5JR1VQ1_Y,6220
18
- crossref_local/_cli/cli.py,sha256=w7rJ4ZtetSr6TRz2f2vyHqZylq5vXM8O9KB61gScbFM,16903
18
+ crossref_local/_cli/cli.py,sha256=TFhj7BjSHxs3rWyOifpi39z6-YmHIHt5Jz-cZNtVtEg,12425
19
19
  crossref_local/_cli/completion.py,sha256=yzqwMbGbbqXJQHdztuaqji-AdFTd8zDDhHeNGdw4NpU,7341
20
20
  crossref_local/_cli/main.py,sha256=NgCGB5-ThofTRPoxxwZpsfCQTo5WIw8oP7zBSKhEiDQ,444
21
21
  crossref_local/_cli/mcp.py,sha256=yjUoq3hvu2OofYf_D29_NXoTvkhxz5QbWAuWv6U-H8U,11427
22
- crossref_local/_cli/mcp_server.py,sha256=jZJwhiTavgTHcJctXpafPgy3aIT49-rdIZ-4i88myeU,10989
23
- crossref_local/_core/__init__.py,sha256=6xKO9y3VOuTF1f83uyQ48HWhszYpPfWZj94t0q0rEgk,940
24
- crossref_local/_core/api.py,sha256=YogfhBHDIqT-hdEI-iDe_-py13Vp6F4GW8q0psLDRyQ,7781
22
+ crossref_local/_cli/mcp_server.py,sha256=mplCUBph9WM_Eq7b-MXgT-vvUH9hf4h_eeHUHTQ7f5c,12526
23
+ crossref_local/_cli/search.py,sha256=bqsWn4nCCW_Glgb7dSXsntP4P5nHQJJCSgehvnV9Bbc,6327
24
+ crossref_local/_core/__init__.py,sha256=xfun82MI9-SuaBqX0zB5liiLmj3AkY5TlksVYY6IaOg,1034
25
+ crossref_local/_core/api.py,sha256=fYkr2VkiAQur3fdysIXP57qk0njWBf9ngg6dt5t63Do,7880
25
26
  crossref_local/_core/citations.py,sha256=4ERCTVkZu6kesZRazaWcTpUUeYz0rs97NwFZAYcLdKA,12765
26
27
  crossref_local/_core/config.py,sha256=T-2EX4EDoCLaOAmViBKGQuNvoJP8x7cvZOvmAcNrQms,5475
27
28
  crossref_local/_core/db.py,sha256=KLlcdNWLnKSexc0cuRZGsBLmJUnXeEaD_CBcXhbPXLg,3832
28
- crossref_local/_core/fts.py,sha256=Ti1EdSsrIQMDFTynyoiqC2bi_nHtPZ6uOyj5ERcxiQk,4515
29
- crossref_local/_core/models.py,sha256=4b8153r7xDBcmTQFsIeSLD1jJm4KvuUkKTLixB7lccM,5562
29
+ crossref_local/_core/export.py,sha256=uM_Qb9WXmn7v4tDOQjBlhJNQY0Y0WbefHxNLrRhe5tI,8899
30
+ crossref_local/_core/fts.py,sha256=Jsj3Cm9pihICk7Rvr9vIZbUsoxKzL0OligFFQxtQDVA,5030
31
+ crossref_local/_core/models.py,sha256=tbo0hIViKM9eJQXzp2APFhRvYd1UusUdJHugpPyNAEo,8697
30
32
  crossref_local/_impact_factor/__init__.py,sha256=pcgVCPogBisANYE5Vp2PHVGPgxoMsSXr-6utqVE97-4,559
31
33
  crossref_local/_impact_factor/calculator.py,sha256=eZ13URAZzPdRyAQpS8zXe_T33e2lm_gQhtoJCXbfIGM,15977
32
34
  crossref_local/_impact_factor/journal_lookup.py,sha256=Ztx6ZeWxfmPvA3KfcW5h_yz01XPstIdk91j3nu2Q-qw,8846
33
35
  crossref_local/_remote/__init__.py,sha256=bTA_bed9XsA0HDArm1o0WagsasxN0atzXdRI6I-r7Pk,1412
34
- crossref_local/_remote/base.py,sha256=gvvvq7uGrG1IOQEZ5ZhWWuakg9Fl_GdkAaU1AfCmtzY,11258
36
+ crossref_local/_remote/base.py,sha256=VlQCPdhRcUynB1IAnJAyfXhxzfsLa0tOSSINAH8lMuI,12160
35
37
  crossref_local/_remote/collections.py,sha256=840HkakS_AtZbmRMYP-GpUX4S5oJ9jaJXzjivQgyozU,5022
36
38
  crossref_local/_server/__init__.py,sha256=4fTk1r__5uWhC6XABVDV7PTPdglFIV32hr9KZ5WbKc8,3869
37
39
  crossref_local/_server/middleware.py,sha256=6tdrfFCOA06i0HIDtrHZthY8DXbKy8rMEWHf-3Ve8-Y,926
38
- crossref_local/_server/models.py,sha256=gjnE-MK4XtfdN_obMyqT5xjNXPa3F-BPrgMrrg908KY,2580
40
+ crossref_local/_server/models.py,sha256=8ylDo3UhNoRGjXg5bmiUvB-BWy_8nahD-PSQSEK_8wk,2965
39
41
  crossref_local/_server/routes_citations.py,sha256=DUpxl4FdZS7NheM93vCD6ntP5I1Hgw_D8mkGBEMBV2Y,2742
40
42
  crossref_local/_server/routes_collections.py,sha256=NaWNTMcswmh87HTZYn5QpzxwtA_c18N3cqy6CrqCBNo,8135
41
43
  crossref_local/_server/routes_compat.py,sha256=kSF4_DKg7lcp67Xii95A1pKDBc3kGcqGD-IPaK6TJA8,2726
42
- crossref_local/_server/routes_works.py,sha256=RxQnkyzoQZtm2n5XTBpZjxjB6ti8GFs-zIzap6VHSgU,3521
44
+ crossref_local/_server/routes_works.py,sha256=YtDKh-xxTCvbDf8K7kwxWszIYjYz-PZzqJH8q13CoFE,5080
43
45
  crossref_local/_server/server.py,sha256=D3SSXQ_cBwMqGd9tLwKK84JBDoisvQwVUuu11yr5zhE,579
44
- crossref_local-0.5.0.dist-info/METADATA,sha256=T2gU3EHRunTfZKRUck9aOrCZAE9HW6VoxjiDdCzyKQs,10748
45
- crossref_local-0.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
46
- crossref_local-0.5.0.dist-info/entry_points.txt,sha256=BZbDvHLHzlKzFc-dqLAFwPrWGmGq5yFuD3vslzbmRnk,111
47
- crossref_local-0.5.0.dist-info/RECORD,,
46
+ crossref_local-0.5.1.dist-info/METADATA,sha256=RXoY2iFOTFVQcDQq0vPIwBpqz1rYPIiA4Uz-kj0-YZM,10748
47
+ crossref_local-0.5.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
48
+ crossref_local-0.5.1.dist-info/entry_points.txt,sha256=BZbDvHLHzlKzFc-dqLAFwPrWGmGq5yFuD3vslzbmRnk,111
49
+ crossref_local-0.5.1.dist-info/RECORD,,