scitex 2.15.2__py3-none-any.whl → 2.15.4__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.
Files changed (77) hide show
  1. scitex/_mcp_resources/__init__.py +2 -0
  2. scitex/_mcp_resources/_scholar.py +148 -0
  3. scitex/_mcp_tools/scholar.py +50 -99
  4. scitex/_mcp_tools/social.py +15 -232
  5. scitex/_mcp_tools/writer.py +7 -17
  6. scitex/canvas/mcp_server.py +16 -3
  7. scitex/capture/mcp_server.py +16 -2
  8. scitex/cli/audio.py +90 -20
  9. scitex/cli/capture.py +120 -0
  10. scitex/cli/introspect.py +19 -12
  11. scitex/cli/plt.py +78 -21
  12. scitex/cli/scholar/__init__.py +160 -2
  13. scitex/cli/scholar/_crossref_scitex.py +25 -266
  14. scitex/cli/scholar/_openalex_scitex.py +55 -0
  15. scitex/cli/social.py +63 -22
  16. scitex/cli/stats.py +121 -3
  17. scitex/cli/writer.py +49 -423
  18. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +90 -0
  19. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +1571 -0
  20. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +6262 -0
  21. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +1274 -0
  22. scitex/dev/plt/data/mpl/dir_ax.txt +459 -0
  23. scitex/introspect/_list_api.py +5 -2
  24. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  25. scitex/scholar/__init__.py +14 -9
  26. scitex/scholar/_mcp/crossref_tool_schemas.py +133 -0
  27. scitex/scholar/_mcp/openalex_handlers.py +212 -0
  28. scitex/scholar/_mcp/openalex_tool_schemas.py +96 -0
  29. scitex/scholar/_mcp/tool_schemas.py +16 -1
  30. scitex/scholar/data/.gitkeep +0 -0
  31. scitex/scholar/data/README.md +44 -0
  32. scitex/scholar/data/bib_files/bibliography.bib +1952 -0
  33. scitex/scholar/data/bib_files/neurovista.bib +277 -0
  34. scitex/scholar/data/bib_files/neurovista_enriched.bib +441 -0
  35. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +441 -0
  36. scitex/scholar/data/bib_files/neurovista_processed.bib +338 -0
  37. scitex/scholar/data/bib_files/openaccess.bib +89 -0
  38. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +2178 -0
  39. scitex/scholar/data/bib_files/pac.bib +698 -0
  40. scitex/scholar/data/bib_files/pac_enriched.bib +1061 -0
  41. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  42. scitex/scholar/data/bib_files/pac_titles.txt +75 -0
  43. scitex/scholar/data/bib_files/paywalled.bib +98 -0
  44. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +58 -0
  45. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +87 -0
  46. scitex/scholar/data/bib_files/seizure_prediction.bib +694 -0
  47. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  48. scitex/scholar/data/bib_files/test_complete_enriched.bib +437 -0
  49. scitex/scholar/data/bib_files/test_final_enriched.bib +437 -0
  50. scitex/scholar/data/bib_files/test_seizure.bib +46 -0
  51. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  52. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  53. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  54. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  55. scitex/scholar/data/impact_factor.db +0 -0
  56. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  57. scitex/scholar/local_dbs/__init__.py +31 -0
  58. scitex/scholar/local_dbs/crossref_scitex.py +30 -0
  59. scitex/scholar/local_dbs/openalex_scitex.py +30 -0
  60. scitex/scholar/mcp_server.py +59 -4
  61. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  62. scitex/stats/mcp_server.py +16 -3
  63. scitex/template/mcp_server.py +16 -3
  64. scitex/ui/mcp_server.py +16 -3
  65. scitex/writer/__init__.py +43 -34
  66. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/METADATA +22 -3
  67. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/RECORD +70 -38
  68. scitex/scholar/crossref_scitex.py +0 -367
  69. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +0 -462
  70. scitex/scholar/url_finder/.tmp/open_url/README.md +0 -223
  71. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +0 -694
  72. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +0 -1160
  73. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +0 -344
  74. scitex/scholar/url_finder/.tmp/open_url/__init__.py +0 -24
  75. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/WHEEL +0 -0
  76. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/entry_points.txt +0 -0
  77. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-29
3
+ # File: src/scitex/scholar/_mcp/openalex_handlers.py
4
+ """OpenAlex-SciTeX handler implementations via openalex-local delegation.
5
+
6
+ These handlers delegate to openalex-local for fast access to 250M+ papers.
7
+ Branded as openalex-scitex to distinguish from official OpenAlex API.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ from datetime import datetime
14
+
15
+ __all__ = [
16
+ "openalex_search_handler",
17
+ "openalex_get_handler",
18
+ "openalex_count_handler",
19
+ "openalex_info_handler",
20
+ ]
21
+
22
+
23
+ def _ensure_openalex():
24
+ """Ensure openalex_scitex module is available."""
25
+ try:
26
+ from scitex.scholar import openalex_scitex
27
+
28
+ return openalex_scitex
29
+ except ImportError as e:
30
+ raise RuntimeError(
31
+ "openalex-local not installed. Install with: pip install openalex-local"
32
+ ) from e
33
+
34
+
35
+ async def openalex_search_handler(
36
+ query: str,
37
+ limit: int = 20,
38
+ offset: int = 0,
39
+ year_min: int | None = None,
40
+ year_max: int | None = None,
41
+ ) -> dict:
42
+ """Search OpenAlex database (250M+ papers) via openalex-local.
43
+
44
+ Args:
45
+ query: Search query string (full-text search)
46
+ limit: Maximum number of results (default: 20)
47
+ offset: Number of results to skip for pagination
48
+ year_min: Minimum publication year filter
49
+ year_max: Maximum publication year filter
50
+ """
51
+ try:
52
+ openalex = _ensure_openalex()
53
+ loop = asyncio.get_running_loop()
54
+
55
+ def do_search():
56
+ # Fetch more results for filtering
57
+ fetch_limit = limit * 2 if (year_min or year_max) else limit
58
+ results = openalex.search(query, limit=fetch_limit, offset=offset)
59
+
60
+ papers = []
61
+ for work in results:
62
+ # Apply year filters
63
+ if year_min and work.year and work.year < year_min:
64
+ continue
65
+ if year_max and work.year and work.year > year_max:
66
+ continue
67
+
68
+ papers.append(
69
+ {
70
+ "doi": work.doi,
71
+ "title": work.title,
72
+ "authors": work.authors[:10] if work.authors else [],
73
+ "year": work.year,
74
+ "journal": work.journal,
75
+ "abstract": (
76
+ work.abstract[:500] + "..."
77
+ if work.abstract and len(work.abstract) > 500
78
+ else work.abstract
79
+ ),
80
+ "citation_count": getattr(work, "citation_count", None),
81
+ "reference_count": getattr(work, "reference_count", None),
82
+ "type": getattr(work, "type", None),
83
+ "openalex_id": getattr(work, "openalex_id", None),
84
+ }
85
+ )
86
+ if len(papers) >= limit:
87
+ break
88
+
89
+ return papers, getattr(results, "total", len(papers))
90
+
91
+ papers, total = await loop.run_in_executor(None, do_search)
92
+
93
+ return {
94
+ "success": True,
95
+ "query": query,
96
+ "total": total,
97
+ "count": len(papers),
98
+ "offset": offset,
99
+ "limit": limit,
100
+ "papers": papers,
101
+ "source": "openalex_local",
102
+ "timestamp": datetime.now().isoformat(),
103
+ }
104
+
105
+ except Exception as e:
106
+ return {"success": False, "error": str(e)}
107
+
108
+
109
+ async def openalex_get_handler(
110
+ doi: str = None,
111
+ openalex_id: str = None,
112
+ ) -> dict:
113
+ """Get a paper by DOI or OpenAlex ID from OpenAlex database.
114
+
115
+ Args:
116
+ doi: DOI of the paper (e.g., '10.1038/nature12373')
117
+ openalex_id: OpenAlex ID (e.g., 'W2100837269')
118
+ """
119
+ if not doi and not openalex_id:
120
+ return {"success": False, "error": "Must provide either doi or openalex_id"}
121
+
122
+ try:
123
+ openalex = _ensure_openalex()
124
+ loop = asyncio.get_running_loop()
125
+
126
+ def do_get():
127
+ identifier = doi or openalex_id
128
+ work = openalex.get(identifier)
129
+ if not work:
130
+ return None
131
+
132
+ result = {
133
+ "doi": work.doi,
134
+ "title": work.title,
135
+ "authors": work.authors,
136
+ "year": work.year,
137
+ "journal": work.journal,
138
+ "abstract": work.abstract,
139
+ "citation_count": getattr(work, "citation_count", None),
140
+ "reference_count": getattr(work, "reference_count", None),
141
+ "type": getattr(work, "type", None),
142
+ "openalex_id": getattr(work, "openalex_id", None),
143
+ "publisher": getattr(work, "publisher", None),
144
+ "url": getattr(work, "url", None),
145
+ }
146
+
147
+ return result
148
+
149
+ result = await loop.run_in_executor(None, do_get)
150
+
151
+ if result is None:
152
+ identifier = doi or openalex_id
153
+ return {
154
+ "success": False,
155
+ "error": f"Paper not found: {identifier}",
156
+ "identifier": identifier,
157
+ }
158
+
159
+ return {
160
+ "success": True,
161
+ "paper": result,
162
+ "source": "openalex_local",
163
+ "timestamp": datetime.now().isoformat(),
164
+ }
165
+
166
+ except Exception as e:
167
+ return {"success": False, "error": str(e)}
168
+
169
+
170
+ async def openalex_count_handler(query: str) -> dict:
171
+ """Count papers matching a search query.
172
+
173
+ Args:
174
+ query: Search query string
175
+ """
176
+ try:
177
+ openalex = _ensure_openalex()
178
+ loop = asyncio.get_running_loop()
179
+
180
+ count = await loop.run_in_executor(None, openalex.count, query)
181
+
182
+ return {
183
+ "success": True,
184
+ "query": query,
185
+ "count": count,
186
+ "source": "openalex_local",
187
+ "timestamp": datetime.now().isoformat(),
188
+ }
189
+
190
+ except Exception as e:
191
+ return {"success": False, "error": str(e)}
192
+
193
+
194
+ async def openalex_info_handler() -> dict:
195
+ """Get information about OpenAlex database configuration and status."""
196
+ try:
197
+ openalex = _ensure_openalex()
198
+ loop = asyncio.get_running_loop()
199
+
200
+ info = await loop.run_in_executor(None, openalex.info)
201
+
202
+ return {
203
+ "success": True,
204
+ "info": info,
205
+ "timestamp": datetime.now().isoformat(),
206
+ }
207
+
208
+ except Exception as e:
209
+ return {"success": False, "error": str(e)}
210
+
211
+
212
+ # EOF
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-29
3
+ # File: src/scitex/scholar/_mcp/openalex_tool_schemas.py
4
+ """OpenAlex-Local tool schemas for MCP server.
5
+
6
+ Provides access to 250M+ papers via openalex-local database.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import mcp.types as types
12
+
13
+ __all__ = ["get_openalex_tool_schemas"]
14
+
15
+
16
+ def get_openalex_tool_schemas() -> list[types.Tool]:
17
+ """Return OpenAlex-Local tool schemas."""
18
+ return [
19
+ types.Tool(
20
+ name="openalex_search",
21
+ description=(
22
+ "Search OpenAlex database (250M+ papers) via openalex-local. "
23
+ "Fast full-text search with year filtering. Includes citation data."
24
+ ),
25
+ inputSchema={
26
+ "type": "object",
27
+ "properties": {
28
+ "query": {
29
+ "type": "string",
30
+ "description": "Search query string (full-text search)",
31
+ },
32
+ "limit": {
33
+ "type": "integer",
34
+ "description": "Maximum number of results",
35
+ "default": 20,
36
+ },
37
+ "offset": {
38
+ "type": "integer",
39
+ "description": "Number of results to skip for pagination",
40
+ "default": 0,
41
+ },
42
+ "year_min": {
43
+ "type": "integer",
44
+ "description": "Minimum publication year filter",
45
+ },
46
+ "year_max": {
47
+ "type": "integer",
48
+ "description": "Maximum publication year filter",
49
+ },
50
+ },
51
+ "required": ["query"],
52
+ },
53
+ ),
54
+ types.Tool(
55
+ name="openalex_get",
56
+ description="Get a paper by DOI or OpenAlex ID from OpenAlex database.",
57
+ inputSchema={
58
+ "type": "object",
59
+ "properties": {
60
+ "doi": {
61
+ "type": "string",
62
+ "description": "DOI of the paper (e.g., '10.1038/nature12373')",
63
+ },
64
+ "openalex_id": {
65
+ "type": "string",
66
+ "description": "OpenAlex ID (e.g., 'W2100837269')",
67
+ },
68
+ },
69
+ },
70
+ ),
71
+ types.Tool(
72
+ name="openalex_count",
73
+ description="Count papers matching a search query in OpenAlex database.",
74
+ inputSchema={
75
+ "type": "object",
76
+ "properties": {
77
+ "query": {
78
+ "type": "string",
79
+ "description": "Search query string",
80
+ },
81
+ },
82
+ "required": ["query"],
83
+ },
84
+ ),
85
+ types.Tool(
86
+ name="openalex_info",
87
+ description="Get OpenAlex database configuration and status.",
88
+ inputSchema={
89
+ "type": "object",
90
+ "properties": {},
91
+ },
92
+ ),
93
+ ]
94
+
95
+
96
+ # EOF
@@ -3,9 +3,14 @@
3
3
  # File: src/scitex/scholar/_mcp.tool_schemas.py
4
4
  # ----------------------------------------
5
5
  """Tool schemas for the scitex-scholar MCP server."""
6
+
6
7
  from __future__ import annotations
8
+
7
9
  import mcp.types as types
10
+
8
11
  __all__ = ["get_tool_schemas"]
12
+
13
+
9
14
  def get_tool_schemas() -> list[types.Tool]:
10
15
  """Return all tool schemas for the Scholar MCP server."""
11
16
  base_schemas = [
@@ -500,6 +505,16 @@ def get_tool_schemas() -> list[types.Tool]:
500
505
  },
501
506
  ),
502
507
  ]
508
+ from .crossref_tool_schemas import get_crossref_tool_schemas
503
509
  from .job_tool_schemas import get_job_tool_schemas
504
- return base_schemas + get_job_tool_schemas()
510
+ from .openalex_tool_schemas import get_openalex_tool_schemas
511
+
512
+ return (
513
+ base_schemas
514
+ + get_job_tool_schemas()
515
+ + get_crossref_tool_schemas()
516
+ + get_openalex_tool_schemas()
517
+ )
518
+
519
+
505
520
  # EOF
File without changes
@@ -0,0 +1,44 @@
1
+ # Scholar Data Directory
2
+
3
+ User-provided data files. This directory is gitignored.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ data/
9
+ └── impact_factor/
10
+ ├── JCR_IF_2024.xlsx # JCR Excel file (user-provided)
11
+ ├── JCR_IF_2024.db # SQLite database (generated)
12
+ └── impact_factor.db -> JCR_IF_2024.db (symlink to active DB)
13
+ ```
14
+
15
+ ## Important
16
+
17
+ - **Data NOT included in git**: This directory is gitignored
18
+ - **User responsibility**: Users must provide their own JCR data
19
+ - **Licensing**: Users must ensure proper licensing for any data
20
+
21
+ ## Adding JCR Data
22
+
23
+ 1. Obtain JCR Excel file from Clarivate or authorized source
24
+ 2. Place in `src/scitex/scholar/data/impact_factor/JCR_IF_YYYY.xlsx`
25
+ 3. Build database (optional - will auto-build if needed):
26
+ ```python
27
+ from scitex.scholar.impact_factor.jcr import build_database
28
+ build_database("JCR_IF_2024.xlsx")
29
+ ```
30
+
31
+ ## File Naming Convention
32
+
33
+ - Excel: `JCR_IF_YYYY.xlsx` (e.g., JCR_IF_2024.xlsx)
34
+ - Database: `JCR_IF_YYYY.db` (e.g., JCR_IF_2024.db)
35
+ - Symlink: `impact_factor.db` → points to current year DB
36
+
37
+ ## Legal Notice
38
+
39
+ JCR data is proprietary (Clarivate Analytics). Users are responsible for:
40
+ - Obtaining data through authorized channels
41
+ - Compliance with licensing terms
42
+ - Not distributing data files
43
+
44
+ We provide only the code to use the data, not the data itself.