scitex 2.14.0__py3-none-any.whl → 2.15.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.
Files changed (218) hide show
  1. scitex/__init__.py +47 -0
  2. scitex/_env_loader.py +156 -0
  3. scitex/_mcp_resources/__init__.py +37 -0
  4. scitex/_mcp_resources/_cheatsheet.py +135 -0
  5. scitex/_mcp_resources/_figrecipe.py +138 -0
  6. scitex/_mcp_resources/_formats.py +102 -0
  7. scitex/_mcp_resources/_modules.py +337 -0
  8. scitex/_mcp_resources/_session.py +149 -0
  9. scitex/_mcp_tools/__init__.py +4 -0
  10. scitex/_mcp_tools/audio.py +66 -0
  11. scitex/_mcp_tools/diagram.py +11 -95
  12. scitex/_mcp_tools/introspect.py +191 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +244 -0
  16. scitex/_mcp_tools/writer.py +21 -204
  17. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  18. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  19. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  20. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  21. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  22. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  23. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  24. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  25. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  26. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  27. scitex/audio/README.md +40 -36
  28. scitex/audio/__init__.py +127 -59
  29. scitex/audio/_branding.py +185 -0
  30. scitex/audio/_mcp/__init__.py +32 -0
  31. scitex/audio/_mcp/handlers.py +59 -6
  32. scitex/audio/_mcp/speak_handlers.py +238 -0
  33. scitex/audio/_relay.py +225 -0
  34. scitex/audio/engines/elevenlabs_engine.py +6 -1
  35. scitex/audio/mcp_server.py +228 -75
  36. scitex/canvas/README.md +1 -1
  37. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  38. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  39. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  40. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  41. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  42. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  43. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  44. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  45. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  46. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  47. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  48. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  49. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  50. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  51. scitex/cli/__init__.py +38 -43
  52. scitex/cli/audio.py +76 -27
  53. scitex/cli/capture.py +13 -20
  54. scitex/cli/introspect.py +443 -0
  55. scitex/cli/main.py +198 -109
  56. scitex/cli/mcp.py +60 -34
  57. scitex/cli/scholar/__init__.py +8 -0
  58. scitex/cli/scholar/_crossref_scitex.py +296 -0
  59. scitex/cli/scholar/_fetch.py +25 -3
  60. scitex/cli/social.py +314 -0
  61. scitex/cli/writer.py +117 -0
  62. scitex/config/README.md +1 -1
  63. scitex/config/__init__.py +16 -2
  64. scitex/config/_env_registry.py +191 -0
  65. scitex/diagram/__init__.py +42 -19
  66. scitex/diagram/mcp_server.py +13 -125
  67. scitex/introspect/__init__.py +75 -0
  68. scitex/introspect/_call_graph.py +303 -0
  69. scitex/introspect/_class_hierarchy.py +163 -0
  70. scitex/introspect/_core.py +42 -0
  71. scitex/introspect/_docstring.py +131 -0
  72. scitex/introspect/_examples.py +113 -0
  73. scitex/introspect/_imports.py +271 -0
  74. scitex/introspect/_mcp/__init__.py +37 -0
  75. scitex/introspect/_mcp/handlers.py +208 -0
  76. scitex/introspect/_members.py +151 -0
  77. scitex/introspect/_resolve.py +89 -0
  78. scitex/introspect/_signature.py +131 -0
  79. scitex/introspect/_source.py +80 -0
  80. scitex/introspect/_type_hints.py +172 -0
  81. scitex/io/bundle/README.md +1 -1
  82. scitex/mcp_server.py +98 -5
  83. scitex/plt/__init__.py +248 -550
  84. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  85. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  86. scitex/plt/gallery/README.md +1 -1
  87. scitex/plt/utils/_hitmap/__init__.py +82 -0
  88. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  89. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  90. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  91. scitex/plt/utils/_hitmap/_constants.py +40 -0
  92. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  93. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  94. scitex/plt/utils/_hitmap/_query.py +113 -0
  95. scitex/plt/utils/_hitmap.py +46 -1616
  96. scitex/plt/utils/_metadata/__init__.py +80 -0
  97. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  98. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  99. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  100. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  101. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  102. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  103. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  104. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  105. scitex/plt/utils/_metadata/_csv.py +416 -0
  106. scitex/plt/utils/_metadata/_detect.py +225 -0
  107. scitex/plt/utils/_metadata/_legend.py +127 -0
  108. scitex/plt/utils/_metadata/_rounding.py +117 -0
  109. scitex/plt/utils/_metadata/_verification.py +202 -0
  110. scitex/schema/README.md +1 -1
  111. scitex/scholar/__init__.py +8 -0
  112. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  113. scitex/scholar/core/Scholar.py +63 -1700
  114. scitex/scholar/core/_mixins/__init__.py +36 -0
  115. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  116. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  117. scitex/scholar/core/_mixins/_loaders.py +103 -0
  118. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  119. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  120. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  121. scitex/scholar/core/_mixins/_savers.py +69 -0
  122. scitex/scholar/core/_mixins/_search.py +103 -0
  123. scitex/scholar/core/_mixins/_services.py +88 -0
  124. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  125. scitex/scholar/crossref_scitex.py +367 -0
  126. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  127. scitex/scholar/examples/00_run_all.sh +120 -0
  128. scitex/scholar/jobs/_executors.py +27 -3
  129. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  130. scitex/scholar/pdf_download/_cli.py +154 -0
  131. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  132. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  133. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  134. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  135. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  136. scitex/scholar/pipelines/_single_steps.py +71 -36
  137. scitex/scholar/storage/_LibraryManager.py +97 -1695
  138. scitex/scholar/storage/_mixins/__init__.py +30 -0
  139. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  140. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  141. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  142. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  143. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  144. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  145. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  146. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  147. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  148. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  149. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  150. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  151. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  152. scitex/security/README.md +3 -3
  153. scitex/session/README.md +1 -1
  154. scitex/sh/README.md +1 -1
  155. scitex/social/__init__.py +153 -0
  156. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  157. scitex/template/README.md +1 -1
  158. scitex/template/clone_writer_directory.py +5 -5
  159. scitex/writer/README.md +1 -1
  160. scitex/writer/_mcp/handlers.py +11 -744
  161. scitex/writer/_mcp/tool_schemas.py +5 -335
  162. scitex-2.15.1.dist-info/METADATA +648 -0
  163. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/RECORD +166 -111
  164. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  165. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  166. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  167. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  168. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  169. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  170. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  171. scitex/diagram/_compile.py +0 -312
  172. scitex/diagram/_diagram.py +0 -355
  173. scitex/diagram/_mcp/__init__.py +0 -4
  174. scitex/diagram/_mcp/handlers.py +0 -400
  175. scitex/diagram/_mcp/tool_schemas.py +0 -157
  176. scitex/diagram/_presets.py +0 -173
  177. scitex/diagram/_schema.py +0 -182
  178. scitex/diagram/_split.py +0 -278
  179. scitex/plt/_mcp/__init__.py +0 -4
  180. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  181. scitex/plt/_mcp/_handlers_figure.py +0 -195
  182. scitex/plt/_mcp/_handlers_plot.py +0 -252
  183. scitex/plt/_mcp/_handlers_style.py +0 -219
  184. scitex/plt/_mcp/handlers.py +0 -74
  185. scitex/plt/_mcp/tool_schemas.py +0 -497
  186. scitex/plt/mcp_server.py +0 -231
  187. scitex/scholar/data/.gitkeep +0 -0
  188. scitex/scholar/data/README.md +0 -44
  189. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  190. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  191. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  192. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  193. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  194. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  195. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  196. scitex/scholar/data/bib_files/pac.bib +0 -698
  197. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  198. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  199. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  200. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  201. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  202. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  203. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  204. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  205. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  206. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  207. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  208. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  209. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  210. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  211. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  212. scitex/scholar/data/impact_factor.db +0 -0
  213. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  214. scitex/scholar/examples/dev.py +0 -38
  215. scitex-2.14.0.dist-info/METADATA +0 -1238
  216. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/WHEEL +0 -0
  217. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/entry_points.txt +0 -0
  218. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,765 +1,32 @@
1
1
  #!/usr/bin/env python3
2
- # Timestamp: 2026-01-09
3
- # File: src/scitex/writer/_mcp.handlers.py
4
- # ----------------------------------------
2
+ # Timestamp: 2026-01-20
3
+ # File: src/scitex/writer/_mcp/handlers.py
5
4
 
6
5
  """
7
- MCP Handler implementations for SciTeX Writer module.
6
+ MCP Handler for SciTeX Writer module.
8
7
 
9
- Provides async handlers for LaTeX manuscript operations:
10
- - clone_project_handler: Create new writer project
11
- - compile_manuscript_handler: Compile manuscript PDF
12
- - compile_supplementary_handler: Compile supplementary PDF
13
- - compile_revision_handler: Compile revision PDF
14
- - get_project_info_handler: Get project information
15
- - get_pdf_handler: Get compiled PDF path
16
- - list_document_types_handler: List document types
8
+ Delegates to scitex-writer package for usage instructions.
17
9
  """
18
10
 
19
11
  from __future__ import annotations
20
12
 
21
- import asyncio
22
- from pathlib import Path
23
- from typing import List, Optional, Union
24
13
 
25
-
26
- async def clone_project_handler(
27
- project_dir: str,
28
- git_strategy: str = "child",
29
- branch: Optional[str] = None,
30
- tag: Optional[str] = None,
31
- ) -> dict:
32
- """
33
- Create a new writer project from template.
34
-
35
- Parameters
36
- ----------
37
- project_dir : str
38
- Path to create project directory
39
- git_strategy : str, optional
40
- Git initialization strategy (child, parent, origin, none)
41
- branch : str, optional
42
- Specific branch to clone
43
- tag : str, optional
44
- Specific tag to clone
45
-
46
- Returns
47
- -------
48
- dict
49
- Success status and project path
50
- """
51
- try:
52
- from scitex.writer._clone_writer_project import clone_writer_project
53
-
54
- # Handle git_strategy='none'
55
- git_strat = None if git_strategy == "none" else git_strategy
56
-
57
- # Run clone in executor (blocking operation)
58
- loop = asyncio.get_event_loop()
59
- success = await loop.run_in_executor(
60
- None,
61
- lambda: clone_writer_project(
62
- project_dir=project_dir,
63
- git_strategy=git_strat,
64
- branch=branch,
65
- tag=tag,
66
- ),
67
- )
68
-
69
- if success:
70
- resolved_path = Path(project_dir)
71
- if not resolved_path.is_absolute():
72
- resolved_path = Path.cwd() / resolved_path
73
-
74
- return {
75
- "success": True,
76
- "project_path": str(resolved_path),
77
- "git_strategy": git_strategy,
78
- "structure": {
79
- "00_shared": "Shared resources (figures, bibliography)",
80
- "01_manuscript": "Main manuscript",
81
- "02_supplementary": "Supplementary materials",
82
- "03_revision": "Revision documents",
83
- "scripts": "Compilation scripts",
84
- },
85
- "message": f"Successfully created writer project at {resolved_path}",
86
- }
87
- else:
88
- return {
89
- "success": False,
90
- "error": "Failed to clone writer project",
91
- "project_dir": project_dir,
92
- }
93
-
94
- except Exception as e:
95
- return {
96
- "success": False,
97
- "error": str(e),
98
- }
99
-
100
-
101
- async def compile_manuscript_handler(
102
- project_dir: str,
103
- timeout: int = 300,
104
- no_figs: bool = False,
105
- ppt2tif: bool = False,
106
- crop_tif: bool = False,
107
- quiet: bool = False,
108
- verbose: bool = False,
109
- force: bool = False,
110
- ) -> dict:
111
- """
112
- Compile manuscript to PDF.
113
-
114
- Parameters
115
- ----------
116
- project_dir : str
117
- Path to writer project directory
118
- timeout : int, optional
119
- Maximum compilation time in seconds
120
- no_figs : bool, optional
121
- Exclude figures for quick compilation
122
- ppt2tif : bool, optional
123
- Convert PowerPoint files to TIF format (WSL only)
124
- crop_tif : bool, optional
125
- Crop TIF images to remove whitespace
126
- quiet : bool, optional
127
- Suppress detailed LaTeX logs
128
- verbose : bool, optional
129
- Show verbose LaTeX output
130
- force : bool, optional
131
- Force recompilation, ignore cache
132
-
133
- Returns
134
- -------
135
- dict
136
- Success status, PDF path, and compilation details
137
- """
138
- try:
139
- from scitex.writer._compile import compile_manuscript
140
-
141
- project_path = Path(project_dir)
142
- if not project_path.is_absolute():
143
- project_path = Path.cwd() / project_path
144
-
145
- # Run compilation in executor
146
- loop = asyncio.get_event_loop()
147
-
148
- def do_compile():
149
- return compile_manuscript(
150
- project_path,
151
- timeout=timeout,
152
- no_figs=no_figs,
153
- ppt2tif=ppt2tif,
154
- crop_tif=crop_tif,
155
- quiet=quiet,
156
- verbose=verbose,
157
- force=force,
158
- )
159
-
160
- result = await loop.run_in_executor(None, do_compile)
161
-
162
- if result.success:
163
- return {
164
- "success": True,
165
- "output_pdf": str(result.output_pdf) if result.output_pdf else None,
166
- "exit_code": result.exit_code,
167
- "warnings": result.warnings[:10] if result.warnings else [],
168
- "message": "Manuscript compiled successfully",
169
- }
170
- else:
171
- return {
172
- "success": False,
173
- "exit_code": result.exit_code,
174
- "errors": result.errors[:10] if result.errors else [],
175
- "warnings": result.warnings[:10] if result.warnings else [],
176
- "error": f"Compilation failed with exit code {result.exit_code}",
177
- }
178
-
179
- except Exception as e:
180
- return {
181
- "success": False,
182
- "error": str(e),
183
- }
184
-
185
-
186
- async def compile_supplementary_handler(
187
- project_dir: str,
188
- timeout: int = 300,
189
- no_figs: bool = False,
190
- ppt2tif: bool = False,
191
- crop_tif: bool = False,
192
- quiet: bool = False,
193
- ) -> dict:
194
- """
195
- Compile supplementary materials to PDF.
196
-
197
- Parameters
198
- ----------
199
- project_dir : str
200
- Path to writer project directory
201
- timeout : int, optional
202
- Maximum compilation time in seconds
203
- no_figs : bool, optional
204
- Exclude figures for quick compilation
205
- ppt2tif : bool, optional
206
- Convert PowerPoint files to TIF format (WSL only)
207
- crop_tif : bool, optional
208
- Crop TIF images to remove whitespace
209
- quiet : bool, optional
210
- Suppress detailed LaTeX logs
211
-
212
- Returns
213
- -------
214
- dict
215
- Success status, PDF path, and compilation details
216
- """
217
- try:
218
- from scitex.writer._compile import compile_supplementary
219
-
220
- project_path = Path(project_dir)
221
- if not project_path.is_absolute():
222
- project_path = Path.cwd() / project_path
223
-
224
- loop = asyncio.get_event_loop()
225
-
226
- def do_compile():
227
- return compile_supplementary(
228
- project_path,
229
- timeout=timeout,
230
- no_figs=no_figs,
231
- ppt2tif=ppt2tif,
232
- crop_tif=crop_tif,
233
- quiet=quiet,
234
- )
235
-
236
- result = await loop.run_in_executor(None, do_compile)
237
-
238
- if result.success:
239
- return {
240
- "success": True,
241
- "output_pdf": str(result.output_pdf) if result.output_pdf else None,
242
- "exit_code": result.exit_code,
243
- "warnings": result.warnings[:10] if result.warnings else [],
244
- "message": "Supplementary materials compiled successfully",
245
- }
246
- else:
247
- return {
248
- "success": False,
249
- "exit_code": result.exit_code,
250
- "errors": result.errors[:10] if result.errors else [],
251
- "warnings": result.warnings[:10] if result.warnings else [],
252
- "error": f"Compilation failed with exit code {result.exit_code}",
253
- }
254
-
255
- except Exception as e:
256
- return {
257
- "success": False,
258
- "error": str(e),
259
- }
260
-
261
-
262
- async def compile_revision_handler(
263
- project_dir: str,
264
- track_changes: bool = False,
265
- timeout: int = 300,
266
- ) -> dict:
267
- """
268
- Compile revision document to PDF.
269
-
270
- Parameters
271
- ----------
272
- project_dir : str
273
- Path to writer project directory
274
- track_changes : bool, optional
275
- Enable change tracking in output
276
- timeout : int, optional
277
- Maximum compilation time in seconds
278
-
279
- Returns
280
- -------
281
- dict
282
- Success status, PDF path, and compilation details
283
- """
284
- try:
285
- from scitex.writer._compile import compile_revision
286
-
287
- project_path = Path(project_dir)
288
- if not project_path.is_absolute():
289
- project_path = Path.cwd() / project_path
290
-
291
- loop = asyncio.get_event_loop()
292
-
293
- def do_compile():
294
- return compile_revision(
295
- project_path,
296
- track_changes=track_changes,
297
- timeout=timeout,
298
- )
299
-
300
- result = await loop.run_in_executor(None, do_compile)
301
-
302
- if result.success:
303
- return {
304
- "success": True,
305
- "output_pdf": str(result.output_pdf) if result.output_pdf else None,
306
- "exit_code": result.exit_code,
307
- "track_changes": track_changes,
308
- "warnings": result.warnings[:10] if result.warnings else [],
309
- "message": "Revision compiled successfully",
310
- }
311
- else:
312
- return {
313
- "success": False,
314
- "exit_code": result.exit_code,
315
- "errors": result.errors[:10] if result.errors else [],
316
- "warnings": result.warnings[:10] if result.warnings else [],
317
- "error": f"Compilation failed with exit code {result.exit_code}",
318
- }
319
-
320
- except Exception as e:
321
- return {
322
- "success": False,
323
- "error": str(e),
324
- }
325
-
326
-
327
- async def get_project_info_handler(project_dir: str) -> dict:
328
- """
329
- Get writer project information.
330
-
331
- Parameters
332
- ----------
333
- project_dir : str
334
- Path to writer project directory
335
-
336
- Returns
337
- -------
338
- dict
339
- Project structure and status information
340
- """
341
- try:
342
- from scitex.writer import Writer
343
-
344
- project_path = Path(project_dir)
345
- if not project_path.is_absolute():
346
- project_path = Path.cwd() / project_path
347
-
348
- loop = asyncio.get_event_loop()
349
-
350
- def get_info():
351
- writer = Writer(project_path)
352
-
353
- # Check for compiled PDFs
354
- pdfs = {}
355
- for doc_type in ["manuscript", "supplementary", "revision"]:
356
- pdf = writer.get_pdf(doc_type)
357
- pdfs[doc_type] = str(pdf) if pdf else None
358
-
359
- return {
360
- "project_name": writer.project_name,
361
- "project_dir": str(writer.project_dir.absolute()),
362
- "git_root": str(writer.git_root) if writer.git_root else None,
363
- "documents": {
364
- "shared": str(writer.shared.root),
365
- "manuscript": str(writer.manuscript.root),
366
- "supplementary": str(writer.supplementary.root),
367
- "revision": str(writer.revision.root),
368
- "scripts": str(writer.scripts.root),
369
- },
370
- "compiled_pdfs": pdfs,
371
- }
372
-
373
- info = await loop.run_in_executor(None, get_info)
374
-
375
- return {
376
- "success": True,
377
- **info,
378
- }
379
-
380
- except Exception as e:
381
- return {
382
- "success": False,
383
- "error": str(e),
384
- }
385
-
386
-
387
- async def get_pdf_handler(
388
- project_dir: str,
389
- doc_type: str = "manuscript",
390
- ) -> dict:
391
- """
392
- Get path to compiled PDF.
393
-
394
- Parameters
395
- ----------
396
- project_dir : str
397
- Path to writer project directory
398
- doc_type : str, optional
399
- Document type (manuscript, supplementary, revision)
400
-
401
- Returns
402
- -------
403
- dict
404
- PDF path if exists
405
- """
406
- try:
407
- from scitex.writer import Writer
408
-
409
- project_path = Path(project_dir)
410
- if not project_path.is_absolute():
411
- project_path = Path.cwd() / project_path
412
-
413
- loop = asyncio.get_event_loop()
414
-
415
- def get_pdf():
416
- writer = Writer(project_path)
417
- return writer.get_pdf(doc_type)
418
-
419
- pdf = await loop.run_in_executor(None, get_pdf)
420
-
421
- if pdf:
422
- return {
423
- "success": True,
424
- "exists": True,
425
- "doc_type": doc_type,
426
- "pdf_path": str(pdf),
427
- }
428
- else:
429
- return {
430
- "success": True,
431
- "exists": False,
432
- "doc_type": doc_type,
433
- "pdf_path": None,
434
- "message": f"No compiled PDF found for {doc_type}",
435
- }
436
-
437
- except Exception as e:
438
- return {
439
- "success": False,
440
- "error": str(e),
441
- }
442
-
443
-
444
- async def list_document_types_handler() -> dict:
445
- """
446
- List available document types in a writer project.
447
-
448
- Returns
449
- -------
450
- dict
451
- List of document types with descriptions
452
- """
453
- return {
454
- "success": True,
455
- "document_types": [
456
- {
457
- "id": "manuscript",
458
- "name": "Manuscript",
459
- "description": "Main manuscript document",
460
- "directory": "01_manuscript",
461
- "compile_command": "compile_manuscript",
462
- },
463
- {
464
- "id": "supplementary",
465
- "name": "Supplementary Materials",
466
- "description": "Supplementary information, figures, and tables",
467
- "directory": "02_supplementary",
468
- "compile_command": "compile_supplementary",
469
- },
470
- {
471
- "id": "revision",
472
- "name": "Revision",
473
- "description": "Revision document with optional change tracking",
474
- "directory": "03_revision",
475
- "compile_command": "compile_revision",
476
- "supports_track_changes": True,
477
- },
478
- ],
479
- "shared_directory": {
480
- "id": "shared",
481
- "name": "Shared Resources",
482
- "description": "Figures, bibliography, and shared assets",
483
- "directory": "00_shared",
484
- },
485
- }
486
-
487
-
488
- async def csv2latex_handler(
489
- csv_path: str,
490
- output_path: Optional[str] = None,
491
- caption: Optional[str] = None,
492
- label: Optional[str] = None,
493
- longtable: bool = False,
494
- ) -> dict:
495
- """
496
- Convert CSV file to LaTeX table.
497
-
498
- Parameters
499
- ----------
500
- csv_path : str
501
- Path to CSV file
502
- output_path : str, optional
503
- Output path for LaTeX file
504
- caption : str, optional
505
- Table caption
506
- label : str, optional
507
- Table label for referencing
508
- longtable : bool, optional
509
- Use longtable for multi-page tables
510
-
511
- Returns
512
- -------
513
- dict
514
- LaTeX content and output path
515
- """
516
- try:
517
- from scitex.writer.utils import csv2latex
518
-
519
- loop = asyncio.get_event_loop()
520
- latex_content = await loop.run_in_executor(
521
- None,
522
- lambda: csv2latex(
523
- csv_path=csv_path,
524
- output_path=output_path,
525
- caption=caption,
526
- label=label,
527
- longtable=longtable,
528
- ),
529
- )
530
-
531
- return {
532
- "success": True,
533
- "latex_content": latex_content,
534
- "output_path": output_path,
535
- "message": f"Converted {csv_path} to LaTeX table",
536
- }
537
-
538
- except Exception as e:
539
- return {
540
- "success": False,
541
- "error": str(e),
542
- }
543
-
544
-
545
- async def latex2csv_handler(
546
- latex_path: str,
547
- output_path: Optional[str] = None,
548
- table_index: int = 0,
549
- ) -> dict:
550
- """
551
- Convert LaTeX table to CSV.
552
-
553
- Parameters
554
- ----------
555
- latex_path : str
556
- Path to LaTeX file containing table
557
- output_path : str, optional
558
- Output path for CSV file
559
- table_index : int, optional
560
- Index of table to extract
561
-
562
- Returns
563
- -------
564
- dict
565
- CSV content preview and output path
566
- """
567
- try:
568
- from scitex.writer.utils import latex2csv
569
-
570
- loop = asyncio.get_event_loop()
571
- df = await loop.run_in_executor(
572
- None,
573
- lambda: latex2csv(
574
- latex_path=latex_path,
575
- output_path=output_path,
576
- table_index=table_index,
577
- ),
578
- )
579
-
580
- return {
581
- "success": True,
582
- "rows": len(df),
583
- "columns": list(df.columns),
584
- "preview": df.head(5).to_dict(),
585
- "output_path": output_path,
586
- "message": f"Converted LaTeX table to CSV ({len(df)} rows)",
587
- }
588
-
589
- except Exception as e:
590
- return {
591
- "success": False,
592
- "error": str(e),
593
- }
594
-
595
-
596
- async def pdf_to_images_handler(
597
- pdf_path: str,
598
- output_dir: Optional[str] = None,
599
- pages: Optional[Union[int, List[int]]] = None,
600
- dpi: int = 150,
601
- format: str = "png",
602
- ) -> dict:
603
- """
604
- Render PDF pages as images.
605
-
606
- Parameters
607
- ----------
608
- pdf_path : str
609
- Path to PDF file
610
- output_dir : str, optional
611
- Output directory for images
612
- pages : int or list of int, optional
613
- Page(s) to render (0-indexed). If None, renders all.
614
- dpi : int, optional
615
- Resolution in DPI
616
- format : str, optional
617
- Output format (png, jpg)
618
-
619
- Returns
620
- -------
621
- dict
622
- List of rendered images with paths
623
- """
624
- try:
625
- from scitex.writer.utils import pdf_to_images
626
-
627
- loop = asyncio.get_event_loop()
628
- images = await loop.run_in_executor(
629
- None,
630
- lambda: pdf_to_images(
631
- pdf_path=pdf_path,
632
- output_dir=output_dir,
633
- pages=pages,
634
- dpi=dpi,
635
- format=format,
636
- ),
637
- )
638
-
639
- return {
640
- "success": True,
641
- "images": images,
642
- "count": len(images),
643
- "message": f"Rendered {len(images)} page(s) from {pdf_path}",
644
- }
645
-
646
- except Exception as e:
647
- return {
648
- "success": False,
649
- "error": str(e),
650
- }
651
-
652
-
653
- async def list_figures_handler(
654
- project_dir: str,
655
- extensions: Optional[list] = None,
656
- ) -> dict:
657
- """
658
- List figures in writer project.
659
-
660
- Parameters
661
- ----------
662
- project_dir : str
663
- Path to writer project directory
664
- extensions : list, optional
665
- File extensions to include
666
-
667
- Returns
668
- -------
669
- dict
670
- List of figure info
671
- """
14
+ async def writer_usage_handler() -> dict:
15
+ """Get usage guide for SciTeX Writer."""
672
16
  try:
673
- from scitex.writer.utils import list_figures
674
-
675
- loop = asyncio.get_event_loop()
676
- figures = await loop.run_in_executor(
677
- None,
678
- lambda: list_figures(
679
- project_dir=project_dir,
680
- extensions=extensions,
681
- ),
682
- )
17
+ from scitex_writer._server import INSTRUCTIONS
683
18
 
684
19
  return {
685
20
  "success": True,
686
- "figures": figures,
687
- "count": len(figures),
688
- "message": f"Found {len(figures)} figures in {project_dir}",
689
- }
690
-
691
- except Exception as e:
692
- return {
693
- "success": False,
694
- "error": str(e),
21
+ "instructions": INSTRUCTIONS,
695
22
  }
696
-
697
-
698
- async def convert_figure_handler(
699
- input_path: str,
700
- output_path: str,
701
- dpi: int = 300,
702
- quality: int = 95,
703
- ) -> dict:
704
- """
705
- Convert figure between formats.
706
-
707
- Parameters
708
- ----------
709
- input_path : str
710
- Input figure path
711
- output_path : str
712
- Output figure path
713
- dpi : int, optional
714
- Resolution for PDF rasterization
715
- quality : int, optional
716
- JPEG quality (1-100)
717
-
718
- Returns
719
- -------
720
- dict
721
- Conversion result with paths
722
- """
723
- try:
724
- from scitex.writer.utils import convert_figure
725
-
726
- loop = asyncio.get_event_loop()
727
- result = await loop.run_in_executor(
728
- None,
729
- lambda: convert_figure(
730
- input_path=input_path,
731
- output_path=output_path,
732
- dpi=dpi,
733
- quality=quality,
734
- ),
735
- )
736
-
737
- return {
738
- "success": True,
739
- **result,
740
- "message": f"Converted {input_path} to {output_path}",
741
- }
742
-
743
- except Exception as e:
23
+ except ImportError:
744
24
  return {
745
25
  "success": False,
746
- "error": str(e),
26
+ "error": "scitex-writer is required. Install with: pip install scitex-writer",
747
27
  }
748
28
 
749
29
 
750
- __all__ = [
751
- "clone_project_handler",
752
- "compile_manuscript_handler",
753
- "compile_supplementary_handler",
754
- "compile_revision_handler",
755
- "get_project_info_handler",
756
- "get_pdf_handler",
757
- "list_document_types_handler",
758
- "csv2latex_handler",
759
- "latex2csv_handler",
760
- "pdf_to_images_handler",
761
- "list_figures_handler",
762
- "convert_figure_handler",
763
- ]
30
+ __all__ = ["writer_usage_handler"]
764
31
 
765
32
  # EOF