rxiv-maker 1.18.5__py3-none-any.whl → 1.19.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.
- rxiv_maker/__version__.py +1 -1
- rxiv_maker/cli/commands/__init__.py +2 -0
- rxiv_maker/cli/commands/biorxiv.py +51 -0
- rxiv_maker/cli/framework/base.py +91 -0
- rxiv_maker/cli/framework/workflow_commands.py +99 -31
- rxiv_maker/cli/main.py +2 -1
- rxiv_maker/engines/operations/prepare_biorxiv.py +401 -0
- {rxiv_maker-1.18.5.dist-info → rxiv_maker-1.19.0.dist-info}/METADATA +1 -1
- {rxiv_maker-1.18.5.dist-info → rxiv_maker-1.19.0.dist-info}/RECORD +12 -10
- {rxiv_maker-1.18.5.dist-info → rxiv_maker-1.19.0.dist-info}/WHEEL +0 -0
- {rxiv_maker-1.18.5.dist-info → rxiv_maker-1.19.0.dist-info}/entry_points.txt +0 -0
- {rxiv_maker-1.18.5.dist-info → rxiv_maker-1.19.0.dist-info}/licenses/LICENSE +0 -0
rxiv_maker/__version__.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .arxiv import arxiv
|
|
4
4
|
from .bibliography import bibliography
|
|
5
|
+
from .biorxiv import biorxiv
|
|
5
6
|
from .build import build as pdf
|
|
6
7
|
from .cache_management import cache_group as cache
|
|
7
8
|
from .changelog import changelog
|
|
@@ -29,6 +30,7 @@ from .version import version
|
|
|
29
30
|
__all__ = [
|
|
30
31
|
"arxiv",
|
|
31
32
|
"bibliography",
|
|
33
|
+
"biorxiv",
|
|
32
34
|
"cache",
|
|
33
35
|
"changelog",
|
|
34
36
|
"config",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""bioRxiv submission package generation command."""
|
|
2
|
+
|
|
3
|
+
import rich_click as click
|
|
4
|
+
|
|
5
|
+
from ..framework.workflow_commands import BioRxivCommand
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
|
9
|
+
@click.argument("manuscript_path", type=click.Path(exists=True, file_okay=False), required=False)
|
|
10
|
+
@click.option("--output-dir", "-o", default="output", help="Output directory for generated files")
|
|
11
|
+
@click.option(
|
|
12
|
+
"--biorxiv-dir",
|
|
13
|
+
"-b",
|
|
14
|
+
help="Custom bioRxiv submission directory (default: output/biorxiv_submission)",
|
|
15
|
+
)
|
|
16
|
+
@click.option(
|
|
17
|
+
"--zip-filename",
|
|
18
|
+
"-z",
|
|
19
|
+
help="Custom ZIP filename (default: {manuscript}_biorxiv.zip)",
|
|
20
|
+
)
|
|
21
|
+
@click.option(
|
|
22
|
+
"--no-zip",
|
|
23
|
+
is_flag=True,
|
|
24
|
+
help="Don't create ZIP file (only create submission directory)",
|
|
25
|
+
)
|
|
26
|
+
@click.pass_context
|
|
27
|
+
def biorxiv(ctx, manuscript_path, output_dir, biorxiv_dir, zip_filename, no_zip):
|
|
28
|
+
r"""Generate bioRxiv submission package.
|
|
29
|
+
|
|
30
|
+
Creates a complete submission package including:
|
|
31
|
+
- bioRxiv author template (TSV file)
|
|
32
|
+
- Manuscript PDF
|
|
33
|
+
- Source files (TeX, figures, bibliography)
|
|
34
|
+
- ZIP file for upload
|
|
35
|
+
|
|
36
|
+
\b
|
|
37
|
+
Example:
|
|
38
|
+
rxiv biorxiv # Full package with ZIP
|
|
39
|
+
rxiv biorxiv --no-zip # Package without ZIP
|
|
40
|
+
rxiv biorxiv -b custom_dir # Custom submission directory
|
|
41
|
+
rxiv biorxiv -z my_submission.zip # Custom ZIP filename
|
|
42
|
+
"""
|
|
43
|
+
command = BioRxivCommand()
|
|
44
|
+
return command.run(
|
|
45
|
+
ctx,
|
|
46
|
+
manuscript_path=manuscript_path,
|
|
47
|
+
output_dir=output_dir,
|
|
48
|
+
biorxiv_dir=biorxiv_dir,
|
|
49
|
+
zip_filename=zip_filename,
|
|
50
|
+
no_zip=no_zip,
|
|
51
|
+
)
|
rxiv_maker/cli/framework/base.py
CHANGED
|
@@ -162,6 +162,97 @@ class BaseCommand(ABC):
|
|
|
162
162
|
if suggestion:
|
|
163
163
|
self.console.print(f"💡 {suggestion}", style="yellow")
|
|
164
164
|
|
|
165
|
+
def _clear_output_directory(self) -> None:
|
|
166
|
+
"""Clear and recreate the output directory.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
CommandExecutionError: If path_manager is not initialized
|
|
170
|
+
"""
|
|
171
|
+
import shutil
|
|
172
|
+
|
|
173
|
+
if not self.path_manager:
|
|
174
|
+
raise CommandExecutionError("Path manager not initialized")
|
|
175
|
+
|
|
176
|
+
if self.path_manager.output_dir.exists():
|
|
177
|
+
shutil.rmtree(self.path_manager.output_dir)
|
|
178
|
+
self.path_manager.output_dir.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
|
|
180
|
+
def _ensure_pdf_built(self, progress_task=None, quiet: bool = True) -> None:
|
|
181
|
+
"""Ensure PDF is built, building it if necessary.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
progress_task: Optional progress task to update
|
|
185
|
+
quiet: Whether to suppress build output
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
CommandExecutionError: If path_manager is not initialized or build fails
|
|
189
|
+
"""
|
|
190
|
+
from ...engines.operations.build_manager import BuildManager
|
|
191
|
+
|
|
192
|
+
if not self.path_manager:
|
|
193
|
+
raise CommandExecutionError("Path manager not initialized")
|
|
194
|
+
|
|
195
|
+
pdf_filename = f"{self.path_manager.manuscript_name}.pdf"
|
|
196
|
+
pdf_path = self.path_manager.output_dir / pdf_filename
|
|
197
|
+
|
|
198
|
+
if not pdf_path.exists():
|
|
199
|
+
if progress_task:
|
|
200
|
+
progress_task.update(description="Building PDF first...")
|
|
201
|
+
|
|
202
|
+
build_manager = BuildManager(
|
|
203
|
+
manuscript_path=str(self.path_manager.manuscript_path),
|
|
204
|
+
output_dir=str(self.path_manager.output_dir),
|
|
205
|
+
verbose=self.verbose,
|
|
206
|
+
quiet=quiet,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
success = build_manager.build()
|
|
211
|
+
if not success:
|
|
212
|
+
raise CommandExecutionError("PDF build failed")
|
|
213
|
+
except Exception as e:
|
|
214
|
+
raise CommandExecutionError(f"Failed to build PDF: {e}") from e
|
|
215
|
+
|
|
216
|
+
def _set_submission_defaults(
|
|
217
|
+
self,
|
|
218
|
+
submission_type: str,
|
|
219
|
+
submission_dir: Optional[str] = None,
|
|
220
|
+
zip_filename: Optional[str] = None,
|
|
221
|
+
) -> tuple[str, str]:
|
|
222
|
+
"""Set default paths for submission directories and ZIP files.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
submission_type: Type of submission ("arxiv" or "biorxiv")
|
|
226
|
+
submission_dir: Custom submission directory path (optional)
|
|
227
|
+
zip_filename: Custom ZIP filename (optional)
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Tuple of (submission_dir, zip_filename) with defaults applied
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
CommandExecutionError: If path_manager is not initialized
|
|
234
|
+
"""
|
|
235
|
+
from pathlib import Path
|
|
236
|
+
|
|
237
|
+
if not self.path_manager:
|
|
238
|
+
raise CommandExecutionError("Path manager not initialized")
|
|
239
|
+
|
|
240
|
+
manuscript_output_dir = str(self.path_manager.output_dir)
|
|
241
|
+
|
|
242
|
+
# Set default submission directory
|
|
243
|
+
if submission_dir is None:
|
|
244
|
+
submission_dir = str(Path(manuscript_output_dir) / f"{submission_type}_submission")
|
|
245
|
+
|
|
246
|
+
# Set default ZIP filename
|
|
247
|
+
if zip_filename is None:
|
|
248
|
+
manuscript_name = self.path_manager.manuscript_name
|
|
249
|
+
if submission_type == "arxiv":
|
|
250
|
+
zip_filename = str(Path(manuscript_output_dir) / "for_arxiv.zip")
|
|
251
|
+
else:
|
|
252
|
+
zip_filename = str(Path(manuscript_output_dir) / f"{manuscript_name}_{submission_type}.zip")
|
|
253
|
+
|
|
254
|
+
return submission_dir, zip_filename
|
|
255
|
+
|
|
165
256
|
@abstractmethod
|
|
166
257
|
def execute_operation(self, **kwargs) -> Any:
|
|
167
258
|
"""Execute the main command operation.
|
|
@@ -366,46 +366,25 @@ class ArxivCommand(BaseCommand):
|
|
|
366
366
|
no_zip: Don't create zip file
|
|
367
367
|
"""
|
|
368
368
|
import sys
|
|
369
|
-
from pathlib import Path
|
|
370
369
|
|
|
371
|
-
from rxiv_maker.engines.operations.build_manager import BuildManager
|
|
372
370
|
from rxiv_maker.engines.operations.prepare_arxiv import main as prepare_arxiv_main
|
|
373
371
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
372
|
+
# Set defaults using shared helper
|
|
373
|
+
arxiv_dir, zip_filename = self._set_submission_defaults("arxiv", arxiv_dir, zip_filename)
|
|
377
374
|
manuscript_output_dir = str(self.path_manager.output_dir)
|
|
378
375
|
|
|
379
|
-
# Set defaults using PathManager
|
|
380
|
-
if arxiv_dir is None:
|
|
381
|
-
arxiv_dir = str(Path(manuscript_output_dir) / "arxiv_submission")
|
|
382
|
-
if zip_filename is None:
|
|
383
|
-
zip_filename = str(Path(manuscript_output_dir) / "for_arxiv.zip")
|
|
384
|
-
|
|
385
376
|
with self.create_progress() as progress:
|
|
386
|
-
# Clear output directory
|
|
377
|
+
# Clear output directory using shared helper
|
|
387
378
|
task = progress.add_task("Clearing output directory...", total=None)
|
|
388
|
-
|
|
389
|
-
shutil.rmtree(self.path_manager.output_dir)
|
|
390
|
-
self.path_manager.output_dir.mkdir(parents=True, exist_ok=True)
|
|
379
|
+
self._clear_output_directory()
|
|
391
380
|
|
|
392
|
-
#
|
|
381
|
+
# Ensure PDF is built using shared helper
|
|
393
382
|
progress.update(task, description="Checking PDF exists...")
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
build_manager = BuildManager(
|
|
400
|
-
manuscript_path=str(self.path_manager.manuscript_path),
|
|
401
|
-
output_dir=str(self.path_manager.output_dir),
|
|
402
|
-
verbose=self.verbose,
|
|
403
|
-
quiet=False,
|
|
404
|
-
)
|
|
405
|
-
success = build_manager.run()
|
|
406
|
-
if not success:
|
|
407
|
-
self.error_message("PDF build failed. Cannot prepare arXiv package.")
|
|
408
|
-
raise CommandExecutionError("PDF build failed")
|
|
383
|
+
try:
|
|
384
|
+
self._ensure_pdf_built(progress_task=task, quiet=False)
|
|
385
|
+
except CommandExecutionError:
|
|
386
|
+
self.error_message("PDF build failed. Cannot prepare arXiv package.")
|
|
387
|
+
raise
|
|
409
388
|
|
|
410
389
|
# Prepare arXiv package
|
|
411
390
|
progress.update(task, description="Preparing arXiv package...")
|
|
@@ -524,6 +503,95 @@ class ArxivCommand(BaseCommand):
|
|
|
524
503
|
return year, first_author
|
|
525
504
|
|
|
526
505
|
|
|
506
|
+
class BioRxivCommand(BaseCommand):
|
|
507
|
+
"""BioRxiv command implementation for generating submission package."""
|
|
508
|
+
|
|
509
|
+
def execute_operation(
|
|
510
|
+
self,
|
|
511
|
+
output_dir: str = "output",
|
|
512
|
+
biorxiv_dir: Optional[str] = None,
|
|
513
|
+
zip_filename: Optional[str] = None,
|
|
514
|
+
no_zip: bool = False,
|
|
515
|
+
) -> None:
|
|
516
|
+
"""Execute bioRxiv submission package preparation.
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
output_dir: Output directory for generated files
|
|
520
|
+
biorxiv_dir: Custom bioRxiv submission directory path
|
|
521
|
+
zip_filename: Custom zip filename
|
|
522
|
+
no_zip: Don't create zip file
|
|
523
|
+
"""
|
|
524
|
+
from pathlib import Path
|
|
525
|
+
|
|
526
|
+
from ...engines.operations.prepare_biorxiv import (
|
|
527
|
+
BioRxivAuthorError,
|
|
528
|
+
create_biorxiv_zip,
|
|
529
|
+
generate_biorxiv_author_tsv,
|
|
530
|
+
prepare_biorxiv_package,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
# Set defaults using shared helper
|
|
534
|
+
biorxiv_dir, zip_filename = self._set_submission_defaults("biorxiv", biorxiv_dir, zip_filename)
|
|
535
|
+
|
|
536
|
+
with self.create_progress() as progress:
|
|
537
|
+
# Clear output directory using shared helper
|
|
538
|
+
task = progress.add_task("Clearing output directory...", total=None)
|
|
539
|
+
self._clear_output_directory()
|
|
540
|
+
|
|
541
|
+
# Ensure PDF is built using shared helper
|
|
542
|
+
progress.update(task, description="Checking PDF exists...")
|
|
543
|
+
self._ensure_pdf_built(progress_task=task, quiet=True)
|
|
544
|
+
|
|
545
|
+
# Generate bioRxiv author template TSV
|
|
546
|
+
progress.update(task, description="Generating bioRxiv author template...")
|
|
547
|
+
output_path = self.path_manager.output_dir
|
|
548
|
+
tsv_file = output_path / "biorxiv_authors.tsv"
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
generate_biorxiv_author_tsv(
|
|
552
|
+
config_path=self.path_manager.get_config_file_path(),
|
|
553
|
+
output_path=tsv_file,
|
|
554
|
+
)
|
|
555
|
+
except BioRxivAuthorError as e:
|
|
556
|
+
progress.update(task, completed=True)
|
|
557
|
+
raise CommandExecutionError(f"Failed to generate bioRxiv template: {e}") from e
|
|
558
|
+
|
|
559
|
+
# Prepare bioRxiv submission package
|
|
560
|
+
progress.update(task, description="Preparing bioRxiv submission package...")
|
|
561
|
+
try:
|
|
562
|
+
biorxiv_path = prepare_biorxiv_package(
|
|
563
|
+
manuscript_path=self.path_manager.manuscript_path,
|
|
564
|
+
output_dir=self.path_manager.output_dir,
|
|
565
|
+
biorxiv_dir=Path(biorxiv_dir),
|
|
566
|
+
)
|
|
567
|
+
except Exception as e:
|
|
568
|
+
progress.update(task, completed=True)
|
|
569
|
+
raise CommandExecutionError(f"Failed to prepare bioRxiv package: {e}") from e
|
|
570
|
+
|
|
571
|
+
# Create ZIP file if requested
|
|
572
|
+
zip_path = None
|
|
573
|
+
if not no_zip:
|
|
574
|
+
progress.update(task, description="Creating ZIP package...")
|
|
575
|
+
try:
|
|
576
|
+
zip_path = create_biorxiv_zip(
|
|
577
|
+
biorxiv_path=biorxiv_path,
|
|
578
|
+
zip_filename=zip_filename,
|
|
579
|
+
manuscript_path=self.path_manager.manuscript_path,
|
|
580
|
+
)
|
|
581
|
+
except Exception as e:
|
|
582
|
+
progress.update(task, completed=True)
|
|
583
|
+
raise CommandExecutionError(f"Failed to create ZIP: {e}") from e
|
|
584
|
+
|
|
585
|
+
progress.update(task, completed=True)
|
|
586
|
+
|
|
587
|
+
# Show success message
|
|
588
|
+
self.console.print("\n[green]✅ bioRxiv submission package ready![/green]")
|
|
589
|
+
self.console.print(f" 📁 Package directory: {biorxiv_path}")
|
|
590
|
+
if zip_path:
|
|
591
|
+
self.console.print(f" 📦 ZIP file: {zip_path}")
|
|
592
|
+
self.console.print("\n📤 Upload to: https://submit.biorxiv.org/")
|
|
593
|
+
|
|
594
|
+
|
|
527
595
|
class TrackChangesCommand(BaseCommand):
|
|
528
596
|
"""Track changes command implementation using the framework."""
|
|
529
597
|
|
rxiv_maker/cli/main.py
CHANGED
|
@@ -60,7 +60,7 @@ click.rich_click.COMMAND_GROUPS = {
|
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"name": "Workflow Commands",
|
|
63
|
-
"commands": ["get-rxiv-preprint", "arxiv", "track-changes", "setup"],
|
|
63
|
+
"commands": ["get-rxiv-preprint", "arxiv", "biorxiv", "track-changes", "setup"],
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
"name": "Configuration",
|
|
@@ -252,6 +252,7 @@ main.add_command(commands.docx)
|
|
|
252
252
|
main.add_command(commands.figures)
|
|
253
253
|
main.add_command(commands.get_rxiv_preprint, name="get-rxiv-preprint")
|
|
254
254
|
main.add_command(commands.arxiv)
|
|
255
|
+
main.add_command(commands.biorxiv)
|
|
255
256
|
main.add_command(commands.init)
|
|
256
257
|
main.add_command(commands.bibliography)
|
|
257
258
|
main.add_command(commands.track_changes)
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"""Prepare bioRxiv author submission template (TSV format).
|
|
2
|
+
|
|
3
|
+
This module generates a tab-separated values (TSV) file containing author information
|
|
4
|
+
formatted for bioRxiv submission system upload.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import csv
|
|
8
|
+
import html.entities
|
|
9
|
+
import logging
|
|
10
|
+
import shutil
|
|
11
|
+
import zipfile
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from ...core.managers.config_manager import ConfigManager
|
|
15
|
+
from ...utils.author_name_formatter import parse_author_name
|
|
16
|
+
from ...utils.email_encoder import decode_email
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def encode_html_entities(text: str) -> str:
|
|
22
|
+
"""Convert Unicode characters to HTML entities for bioRxiv submission.
|
|
23
|
+
|
|
24
|
+
bioRxiv's TSV upload requires special characters to be encoded as HTML entities.
|
|
25
|
+
For example, "António" becomes "António", "Åbo" becomes "Åbo".
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
text: Text that may contain Unicode characters
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Text with Unicode characters converted to HTML entities
|
|
32
|
+
(e.g., "António" -> "António", "Åbo" -> "Åbo")
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
>>> encode_html_entities("António")
|
|
36
|
+
'António'
|
|
37
|
+
>>> encode_html_entities("Åbo")
|
|
38
|
+
'Åbo'
|
|
39
|
+
>>> encode_html_entities("José García")
|
|
40
|
+
'José García'
|
|
41
|
+
"""
|
|
42
|
+
if not text:
|
|
43
|
+
return text
|
|
44
|
+
|
|
45
|
+
# Build reverse mapping: Unicode character -> HTML entity name
|
|
46
|
+
char_to_entity = {}
|
|
47
|
+
for entity_name, codepoint in html.entities.name2codepoint.items():
|
|
48
|
+
char = chr(codepoint)
|
|
49
|
+
# Skip basic ASCII characters and use named entities for special chars
|
|
50
|
+
if ord(char) > 127:
|
|
51
|
+
char_to_entity[char] = f"&{entity_name};"
|
|
52
|
+
|
|
53
|
+
# Convert each character to HTML entity if it has one
|
|
54
|
+
result = []
|
|
55
|
+
for char in text:
|
|
56
|
+
if char in char_to_entity:
|
|
57
|
+
result.append(char_to_entity[char])
|
|
58
|
+
else:
|
|
59
|
+
result.append(char)
|
|
60
|
+
|
|
61
|
+
return "".join(result)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class BioRxivAuthorError(Exception):
|
|
65
|
+
"""Exception raised for bioRxiv author template generation errors."""
|
|
66
|
+
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def validate_author_data(authors: list[dict]) -> None:
|
|
71
|
+
"""Validate author data for bioRxiv submission requirements.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
authors: List of author dictionaries from config
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
BioRxivAuthorError: If validation fails
|
|
78
|
+
"""
|
|
79
|
+
if not authors:
|
|
80
|
+
raise BioRxivAuthorError("No authors found in configuration")
|
|
81
|
+
|
|
82
|
+
# Count corresponding authors
|
|
83
|
+
corresponding_count = sum(1 for author in authors if author.get("corresponding_author", False))
|
|
84
|
+
|
|
85
|
+
if corresponding_count == 0:
|
|
86
|
+
raise BioRxivAuthorError(
|
|
87
|
+
"No corresponding author found. "
|
|
88
|
+
"Exactly one author must be marked with 'corresponding_author: true' in 00_CONFIG.yml"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if corresponding_count > 1:
|
|
92
|
+
corresponding_names = [
|
|
93
|
+
author.get("name", "Unknown") for author in authors if author.get("corresponding_author", False)
|
|
94
|
+
]
|
|
95
|
+
raise BioRxivAuthorError(
|
|
96
|
+
f"Multiple corresponding authors found: {', '.join(corresponding_names)}. "
|
|
97
|
+
"Only one author should be marked with 'corresponding_author: true' in 00_CONFIG.yml"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Validate each author has a name
|
|
101
|
+
for i, author in enumerate(authors):
|
|
102
|
+
if not author.get("name"):
|
|
103
|
+
raise BioRxivAuthorError(f"Author at index {i} is missing the 'name' field")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def format_author_row(author_data: dict, affiliation_map: dict) -> list[str]:
|
|
107
|
+
"""Format a single author's data as a bioRxiv TSV row.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
author_data: Author dictionary with processed data
|
|
111
|
+
affiliation_map: Dictionary mapping affiliation shortnames to full data
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List of column values in bioRxiv order:
|
|
115
|
+
Email, Institution, First Name, Middle Name(s)/Initial(s), Last Name, Suffix,
|
|
116
|
+
Corresponding Author, Home Page URL, Collaborative Group/Consortium, ORCiD
|
|
117
|
+
"""
|
|
118
|
+
# Email (decoded from email64)
|
|
119
|
+
email = author_data.get("email", "")
|
|
120
|
+
|
|
121
|
+
# Institution (first affiliation's full_name) - encode HTML entities for bioRxiv
|
|
122
|
+
institution = ""
|
|
123
|
+
affiliations = author_data.get("affiliations", [])
|
|
124
|
+
if affiliations and affiliations[0] in affiliation_map:
|
|
125
|
+
institution = encode_html_entities(affiliation_map[affiliations[0]].get("full_name", ""))
|
|
126
|
+
|
|
127
|
+
# Parse name into components and encode HTML entities for bioRxiv
|
|
128
|
+
name_str = author_data.get("name", "")
|
|
129
|
+
name_parts = parse_author_name(name_str)
|
|
130
|
+
|
|
131
|
+
first_name = encode_html_entities(name_parts.get("first", ""))
|
|
132
|
+
middle_name = encode_html_entities(name_parts.get("middle", ""))
|
|
133
|
+
last_name = encode_html_entities(name_parts.get("last", ""))
|
|
134
|
+
suffix = encode_html_entities(name_parts.get("suffix", ""))
|
|
135
|
+
|
|
136
|
+
# Corresponding author (any text for Yes, empty string for No)
|
|
137
|
+
corresponding = "Yes" if author_data.get("corresponding_author", False) else ""
|
|
138
|
+
|
|
139
|
+
# Home Page URL (empty - user preference)
|
|
140
|
+
home_page_url = ""
|
|
141
|
+
|
|
142
|
+
# Collaborative Group/Consortium (empty)
|
|
143
|
+
collaborative_group = ""
|
|
144
|
+
|
|
145
|
+
# ORCiD (if present)
|
|
146
|
+
orcid = author_data.get("orcid", "")
|
|
147
|
+
|
|
148
|
+
return [
|
|
149
|
+
email,
|
|
150
|
+
institution,
|
|
151
|
+
first_name,
|
|
152
|
+
middle_name,
|
|
153
|
+
last_name,
|
|
154
|
+
suffix,
|
|
155
|
+
corresponding,
|
|
156
|
+
home_page_url,
|
|
157
|
+
collaborative_group,
|
|
158
|
+
orcid,
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def generate_biorxiv_author_tsv(config_path: Path, output_path: Path) -> Path:
|
|
163
|
+
"""Generate bioRxiv author submission template (TSV format).
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
config_path: Path to the manuscript 00_CONFIG.yml file
|
|
167
|
+
output_path: Path where the TSV file should be written
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Path to the generated TSV file
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
BioRxivAuthorError: If author data is invalid or missing
|
|
174
|
+
FileNotFoundError: If config file doesn't exist
|
|
175
|
+
"""
|
|
176
|
+
if not config_path.exists():
|
|
177
|
+
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
|
178
|
+
|
|
179
|
+
# Load manuscript configuration
|
|
180
|
+
config_manager = ConfigManager(config_path.parent)
|
|
181
|
+
config = config_manager.load_config(config_path)
|
|
182
|
+
|
|
183
|
+
# Extract authors and affiliations
|
|
184
|
+
authors = config.get("authors", [])
|
|
185
|
+
affiliations = config.get("affiliations", [])
|
|
186
|
+
|
|
187
|
+
# Handle multiple corresponding authors: keep only the last one
|
|
188
|
+
corresponding_indices = [i for i, author in enumerate(authors) if author.get("corresponding_author", False)]
|
|
189
|
+
if len(corresponding_indices) > 1:
|
|
190
|
+
# Unmark all but the last corresponding author
|
|
191
|
+
for idx in corresponding_indices[:-1]:
|
|
192
|
+
authors[idx]["corresponding_author"] = False
|
|
193
|
+
logger.warning(
|
|
194
|
+
f"Multiple corresponding authors found. Only keeping the last one: "
|
|
195
|
+
f"{authors[corresponding_indices[-1]].get('name', 'Unknown')}"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Validate author data
|
|
199
|
+
validate_author_data(authors)
|
|
200
|
+
|
|
201
|
+
# Build affiliation map (shortname -> full data)
|
|
202
|
+
affiliation_map = {}
|
|
203
|
+
for affiliation in affiliations:
|
|
204
|
+
shortname = affiliation.get("shortname", "")
|
|
205
|
+
if shortname:
|
|
206
|
+
affiliation_map[shortname] = affiliation
|
|
207
|
+
|
|
208
|
+
# Process authors: decode emails
|
|
209
|
+
processed_authors = []
|
|
210
|
+
for author in authors:
|
|
211
|
+
author_copy = author.copy()
|
|
212
|
+
|
|
213
|
+
# Decode email64 if present
|
|
214
|
+
if "email64" in author_copy:
|
|
215
|
+
try:
|
|
216
|
+
author_copy["email"] = decode_email(author_copy["email64"])
|
|
217
|
+
except ValueError as e:
|
|
218
|
+
logger.warning(f"Failed to decode email64 for {author_copy.get('name', 'Unknown')}: {e}")
|
|
219
|
+
author_copy["email"] = ""
|
|
220
|
+
elif "email" not in author_copy:
|
|
221
|
+
author_copy["email"] = ""
|
|
222
|
+
|
|
223
|
+
processed_authors.append(author_copy)
|
|
224
|
+
|
|
225
|
+
# Generate TSV file
|
|
226
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
227
|
+
|
|
228
|
+
with open(output_path, "w", newline="", encoding="utf-8") as f:
|
|
229
|
+
writer = csv.writer(f, delimiter="\t", quoting=csv.QUOTE_MINIMAL, lineterminator="\n")
|
|
230
|
+
|
|
231
|
+
# Write header row
|
|
232
|
+
header = [
|
|
233
|
+
"Email",
|
|
234
|
+
"Institution",
|
|
235
|
+
"First Name",
|
|
236
|
+
"Middle Name(s)/Initial(s)",
|
|
237
|
+
"Last Name",
|
|
238
|
+
"Suffix",
|
|
239
|
+
"Corresponding Author",
|
|
240
|
+
"Home Page URL",
|
|
241
|
+
"Collaborative Group/Consortium",
|
|
242
|
+
"ORCiD",
|
|
243
|
+
]
|
|
244
|
+
writer.writerow(header)
|
|
245
|
+
|
|
246
|
+
# Write author rows
|
|
247
|
+
for author in processed_authors:
|
|
248
|
+
row = format_author_row(author, affiliation_map)
|
|
249
|
+
writer.writerow(row)
|
|
250
|
+
|
|
251
|
+
logger.info(f"Generated bioRxiv author template: {output_path}")
|
|
252
|
+
return output_path
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def prepare_biorxiv_package(
|
|
256
|
+
manuscript_path: Path,
|
|
257
|
+
output_dir: Path,
|
|
258
|
+
biorxiv_dir: Path | None = None,
|
|
259
|
+
) -> Path:
|
|
260
|
+
"""Prepare bioRxiv submission package.
|
|
261
|
+
|
|
262
|
+
Creates a directory containing:
|
|
263
|
+
- biorxiv_authors.tsv (author template)
|
|
264
|
+
- manuscript PDF
|
|
265
|
+
- source files (TeX, figures, bibliography)
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
manuscript_path: Path to the manuscript directory
|
|
269
|
+
output_dir: Path to the rxiv-maker output directory
|
|
270
|
+
biorxiv_dir: Path where bioRxiv submission files will be created.
|
|
271
|
+
If None, defaults to {output_dir}/biorxiv_submission
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Path to the bioRxiv submission directory
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
FileNotFoundError: If required files are missing
|
|
278
|
+
"""
|
|
279
|
+
output_path = Path(output_dir)
|
|
280
|
+
|
|
281
|
+
# Default bioRxiv directory to be inside the output directory
|
|
282
|
+
if biorxiv_dir is None:
|
|
283
|
+
biorxiv_dir = output_path / "biorxiv_submission"
|
|
284
|
+
|
|
285
|
+
biorxiv_path = Path(biorxiv_dir)
|
|
286
|
+
|
|
287
|
+
# Create clean bioRxiv directory
|
|
288
|
+
if biorxiv_path.exists():
|
|
289
|
+
shutil.rmtree(biorxiv_path)
|
|
290
|
+
biorxiv_path.mkdir(parents=True)
|
|
291
|
+
|
|
292
|
+
manuscript_name = manuscript_path.name if manuscript_path else "manuscript"
|
|
293
|
+
logger.info(f"Preparing bioRxiv submission package for '{manuscript_name}' in {biorxiv_path}")
|
|
294
|
+
|
|
295
|
+
# 1. Copy the bioRxiv authors TSV file (already generated)
|
|
296
|
+
tsv_source = output_path / "biorxiv_authors.tsv"
|
|
297
|
+
if not tsv_source.exists():
|
|
298
|
+
raise FileNotFoundError(
|
|
299
|
+
f"bioRxiv author template not found: {tsv_source}\n"
|
|
300
|
+
"Please run TSV generation first or ensure output directory is correct."
|
|
301
|
+
)
|
|
302
|
+
shutil.copy2(tsv_source, biorxiv_path / "biorxiv_authors.tsv")
|
|
303
|
+
logger.info("✓ Copied author template: biorxiv_authors.tsv")
|
|
304
|
+
|
|
305
|
+
# 2. Find and copy the manuscript PDF
|
|
306
|
+
pdf_files = list(output_path.glob("*.pdf"))
|
|
307
|
+
main_pdf = None
|
|
308
|
+
for pdf in pdf_files:
|
|
309
|
+
# Skip supplementary PDFs
|
|
310
|
+
if "supplementary" not in pdf.name.lower():
|
|
311
|
+
main_pdf = pdf
|
|
312
|
+
break
|
|
313
|
+
|
|
314
|
+
if not main_pdf:
|
|
315
|
+
logger.warning("⚠ No manuscript PDF found in output directory")
|
|
316
|
+
else:
|
|
317
|
+
shutil.copy2(main_pdf, biorxiv_path / main_pdf.name)
|
|
318
|
+
logger.info(f"✓ Copied manuscript PDF: {main_pdf.name}")
|
|
319
|
+
|
|
320
|
+
# 3. Copy source files for submission
|
|
321
|
+
# Copy TeX files
|
|
322
|
+
tex_files = list(output_path.glob("*.tex"))
|
|
323
|
+
for tex_file in tex_files:
|
|
324
|
+
shutil.copy2(tex_file, biorxiv_path / tex_file.name)
|
|
325
|
+
logger.info(f"✓ Copied TeX file: {tex_file.name}")
|
|
326
|
+
|
|
327
|
+
# Copy style file
|
|
328
|
+
style_file = output_path / "rxiv_maker_style.cls"
|
|
329
|
+
if style_file.exists():
|
|
330
|
+
shutil.copy2(style_file, biorxiv_path / "rxiv_maker_style.cls")
|
|
331
|
+
logger.info("✓ Copied style file: rxiv_maker_style.cls")
|
|
332
|
+
|
|
333
|
+
# Copy bibliography
|
|
334
|
+
bib_file = output_path / "03_REFERENCES.bib"
|
|
335
|
+
if bib_file.exists():
|
|
336
|
+
shutil.copy2(bib_file, biorxiv_path / "03_REFERENCES.bib")
|
|
337
|
+
logger.info("✓ Copied bibliography: 03_REFERENCES.bib")
|
|
338
|
+
|
|
339
|
+
# Copy FIGURES directory
|
|
340
|
+
figures_source = output_path / "FIGURES"
|
|
341
|
+
if figures_source.exists() and figures_source.is_dir():
|
|
342
|
+
figures_dest = biorxiv_path / "FIGURES"
|
|
343
|
+
shutil.copytree(figures_source, figures_dest)
|
|
344
|
+
figure_count = len(list(figures_dest.rglob("*")))
|
|
345
|
+
logger.info(f"✓ Copied FIGURES directory ({figure_count} files)")
|
|
346
|
+
|
|
347
|
+
logger.info(f"\n📦 bioRxiv package prepared in {biorxiv_path}")
|
|
348
|
+
return biorxiv_path
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def create_biorxiv_zip(
|
|
352
|
+
biorxiv_path: Path,
|
|
353
|
+
zip_filename: str = "biorxiv_submission.zip",
|
|
354
|
+
manuscript_path: Path | None = None,
|
|
355
|
+
) -> Path:
|
|
356
|
+
"""Create a ZIP file for bioRxiv submission.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
biorxiv_path: Path to the bioRxiv submission directory
|
|
360
|
+
zip_filename: Name of the ZIP file to create
|
|
361
|
+
manuscript_path: Optional manuscript path for naming
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Path to the created ZIP file
|
|
365
|
+
"""
|
|
366
|
+
# Use manuscript-aware naming if manuscript path is provided
|
|
367
|
+
if manuscript_path and zip_filename == "biorxiv_submission.zip":
|
|
368
|
+
manuscript_name = manuscript_path.name
|
|
369
|
+
zip_filename = f"{manuscript_name}_biorxiv.zip"
|
|
370
|
+
|
|
371
|
+
zip_path = Path(zip_filename).resolve()
|
|
372
|
+
|
|
373
|
+
# Define auxiliary files that should be excluded
|
|
374
|
+
auxiliary_extensions = {".aux", ".blg", ".log", ".out", ".fls", ".fdb_latexmk", ".synctex.gz"}
|
|
375
|
+
|
|
376
|
+
logger.info(f"\n📁 Creating ZIP package: {zip_path}")
|
|
377
|
+
|
|
378
|
+
excluded_files = []
|
|
379
|
+
included_files = []
|
|
380
|
+
|
|
381
|
+
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
382
|
+
for file_path in biorxiv_path.rglob("*"):
|
|
383
|
+
if file_path.is_file():
|
|
384
|
+
# Check if file should be excluded (auxiliary files)
|
|
385
|
+
should_exclude = file_path.suffix.lower() in auxiliary_extensions
|
|
386
|
+
|
|
387
|
+
if should_exclude:
|
|
388
|
+
excluded_files.append(file_path.name)
|
|
389
|
+
continue
|
|
390
|
+
|
|
391
|
+
# Store files with relative paths
|
|
392
|
+
arcname = file_path.relative_to(biorxiv_path)
|
|
393
|
+
zipf.write(file_path, arcname)
|
|
394
|
+
included_files.append(str(arcname))
|
|
395
|
+
|
|
396
|
+
logger.info(f"✅ ZIP created: {zip_path}")
|
|
397
|
+
logger.info(f" Files included: {len(included_files)}")
|
|
398
|
+
if excluded_files:
|
|
399
|
+
logger.info(f" Files excluded: {len(excluded_files)} (auxiliary files)")
|
|
400
|
+
|
|
401
|
+
return zip_path
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rxiv-maker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: Write scientific preprints in Markdown. Generate publication-ready PDFs efficiently.
|
|
5
5
|
Project-URL: Homepage, https://github.com/HenriquesLab/rxiv-maker
|
|
6
6
|
Project-URL: Documentation, https://github.com/HenriquesLab/rxiv-maker#readme
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
rxiv_maker/__init__.py,sha256=p04JYC5ZhP6dLXkoWVlKNyiRvsDE1a4C88f9q4xO3tA,3268
|
|
2
|
-
rxiv_maker/__version__.py,sha256=
|
|
2
|
+
rxiv_maker/__version__.py,sha256=s7-5Jsk7L8O8kxknnMqV6RyUG2rsUkOD8DjGMhagzNo,51
|
|
3
3
|
rxiv_maker/rxiv_maker_cli.py,sha256=9Lu_mhFPXwx5jzAR6StCNxwCm_fkmP5qiOYdNuh_AwI,120
|
|
4
4
|
rxiv_maker/validate.py,sha256=AIzgP59KbCQJqC9WIGfUdVv0xI6ud9g1fFznQkaGz5Q,9373
|
|
5
5
|
rxiv_maker/cli/__init__.py,sha256=Jw0DTFUSofN-02xpVrt1UUzRcgH5NNd-GPNidhmNwpU,77
|
|
@@ -7,10 +7,11 @@ rxiv_maker/cli/__main__.py,sha256=OEaQSb9q7MK3MMkEJfOdzzJuDvr5LAeILtHQesdYxWU,13
|
|
|
7
7
|
rxiv_maker/cli/framework.py,sha256=enBb4d48ZsgB-4H6zjzSGnMFEgPd1T7cnUjNcOOPKKY,3895
|
|
8
8
|
rxiv_maker/cli/interactive.py,sha256=lgfZVFf1rdJKZtHPogNiEPSwhMlnU2_dSFU9CJjM-7E,18907
|
|
9
9
|
rxiv_maker/cli/interactive_prompts.py,sha256=jhyCdP54TIYHzgAD84lwcyat3tCGVeK9vmyfLHuwL6I,11830
|
|
10
|
-
rxiv_maker/cli/main.py,sha256=
|
|
11
|
-
rxiv_maker/cli/commands/__init__.py,sha256=
|
|
10
|
+
rxiv_maker/cli/main.py,sha256=ieSKR954s2ppE5SiwDYa5Xwr3vh_xzQxwI1tf2tMcYE,8743
|
|
11
|
+
rxiv_maker/cli/commands/__init__.py,sha256=Kcof7u2l2XE1M4g3AAuio5cTX6WSt4ly1ROujHH54xw,1440
|
|
12
12
|
rxiv_maker/cli/commands/arxiv.py,sha256=nlAS36lgTNjd6Hn1cdporXFZskf7-Jl-fZLvjxc6gjo,1245
|
|
13
13
|
rxiv_maker/cli/commands/bibliography.py,sha256=3a4gNtY7Lvd5-mwIj-vCD5WwRDgMqPT37tJETthKYKA,2956
|
|
14
|
+
rxiv_maker/cli/commands/biorxiv.py,sha256=nKu7nAeTVeWH_3Q-gQnY5Y_Q9YnXZEC50MqQzEwqua4,1670
|
|
14
15
|
rxiv_maker/cli/commands/build.py,sha256=v513o3duOD9YvKUoOTggqshqQzwD4THBVIAClJTnx60,3525
|
|
15
16
|
rxiv_maker/cli/commands/cache_management.py,sha256=y58QsuSjzCz_IhY6iXnir8OoblHW5ZBMqItfnfjTP-Y,4577
|
|
16
17
|
rxiv_maker/cli/commands/changelog.py,sha256=OzEay8E8sWfXagjXyWiKwfE-OC9UKokWB7mbINp36t4,8461
|
|
@@ -33,13 +34,13 @@ rxiv_maker/cli/commands/upgrade.py,sha256=UpdqEQwbNYmDMbSrYGv_pVd-7u8PPT3US5RVEN
|
|
|
33
34
|
rxiv_maker/cli/commands/validate.py,sha256=3JghFQevJvQDQII4p_QWbQXMEUyDpM-t9-WxtaT4edo,1629
|
|
34
35
|
rxiv_maker/cli/commands/version.py,sha256=VMlfSxxsrZH02d24MXLUDZfHBW39yZRpWxUpMhQ-X0Y,2737
|
|
35
36
|
rxiv_maker/cli/framework/__init__.py,sha256=4FPXdP8J6v4eeEn46mwY0VtnwxjR1jnW_kTrXykQlQs,2704
|
|
36
|
-
rxiv_maker/cli/framework/base.py,sha256=
|
|
37
|
+
rxiv_maker/cli/framework/base.py,sha256=HqjVHVJzBKLk9-lH6PQrVxYedvs1efcLZ94IxVq7rmE,11242
|
|
37
38
|
rxiv_maker/cli/framework/cache_commands.py,sha256=J91UYLTsLTRoNdzuhAbNL2bJJovYYfX3T9jI8cNUBWU,9897
|
|
38
39
|
rxiv_maker/cli/framework/config_commands.py,sha256=a1uOQkCCw3d4qlro3OwHIorcoNg03T_R4-HbfVb-hmQ,19336
|
|
39
40
|
rxiv_maker/cli/framework/content_commands.py,sha256=RilxKeG2c1m2fu0CtWAvP3cGh11DGx9P-nh2kIewAg4,22596
|
|
40
41
|
rxiv_maker/cli/framework/decorators.py,sha256=fh085e3k1CaLSMoZevt8hvgnEuejrf-mcNS-dwXoY_A,10365
|
|
41
42
|
rxiv_maker/cli/framework/utility_commands.py,sha256=drIAc1TAYpne76gj7SZeZhPozVAY5uL9GFPVT_Ez0-E,26437
|
|
42
|
-
rxiv_maker/cli/framework/workflow_commands.py,sha256=
|
|
43
|
+
rxiv_maker/cli/framework/workflow_commands.py,sha256=CguePd76EjdT53ab9a9clv3S-FSpBAXioQKmto1VS3w,35422
|
|
43
44
|
rxiv_maker/config/defaults.py,sha256=vHyLGVxe5-z9TLxu5f6NhquPvqQkER_KZv_j1I4_dHQ,3055
|
|
44
45
|
rxiv_maker/config/validator.py,sha256=9XDPfo_YgasGt6NLkl6HIhaGh1fr6XsFNiXU2DSsivw,38299
|
|
45
46
|
rxiv_maker/converters/__init__.py,sha256=d7WGsRwWqRQWO117IkKDP0Ap0ERiK0N2-dXHInye3_A,685
|
|
@@ -99,6 +100,7 @@ rxiv_maker/engines/operations/generate_docs.py,sha256=8d_oVYUuRRqTuYN1KnJKqM5Ydp
|
|
|
99
100
|
rxiv_maker/engines/operations/generate_figures.py,sha256=YeKzH6qVsuPGjtCsvWugLJoys6y73xTyO7Y5g30KM20,38730
|
|
100
101
|
rxiv_maker/engines/operations/generate_preprint.py,sha256=wpKDAu2RLJ4amSdhX5GZ7hU-iTsTRt4etcEA7AZYp04,2662
|
|
101
102
|
rxiv_maker/engines/operations/prepare_arxiv.py,sha256=cd0JN5IO-Wy9T8ab75eibyaA8_K8Gpwrz2F-95OMnx4,21551
|
|
103
|
+
rxiv_maker/engines/operations/prepare_biorxiv.py,sha256=-Ok4iwnLsgNk6xJ9HSk74_7UBammJRTF3r8ISy_qDjc,13858
|
|
102
104
|
rxiv_maker/engines/operations/setup_environment.py,sha256=gERuThHTldH0YqgXn85995deHBP6csY1ZhCNgU6-vFg,12691
|
|
103
105
|
rxiv_maker/engines/operations/track_changes.py,sha256=jJZ-XnTFx8TMvcnX8_9D7ydc0G01S1PnckLkxHRTX1g,24722
|
|
104
106
|
rxiv_maker/engines/operations/validate.py,sha256=OVmtRVtG-r1hoA8IqYaNC-ijN1a5ixM3X5Z8Gda-O2M,17142
|
|
@@ -194,8 +196,8 @@ rxiv_maker/validators/doi/metadata_comparator.py,sha256=euqHhKP5sHQAdZbdoAahUn6Y
|
|
|
194
196
|
rxiv_maker/tex/template.tex,sha256=_tPtxrurn3sKTt9Kfa44lPdPyT44vHbDUOGqldU9r2s,1378
|
|
195
197
|
rxiv_maker/tex/style/rxiv_maker_style.bst,sha256=jbVqrJgAm6F88cow5vtZuPBwwmlcYykclTm8RvZIo6Y,24281
|
|
196
198
|
rxiv_maker/tex/style/rxiv_maker_style.cls,sha256=6VDmZE0uvYWog6rcYi2K_NIM9-Pgjx9AFdRg_sTheK0,24374
|
|
197
|
-
rxiv_maker-1.
|
|
198
|
-
rxiv_maker-1.
|
|
199
|
-
rxiv_maker-1.
|
|
200
|
-
rxiv_maker-1.
|
|
201
|
-
rxiv_maker-1.
|
|
199
|
+
rxiv_maker-1.19.0.dist-info/METADATA,sha256=cM3QcAip52NGWPYbS5RqFTaP1Q3L6nsN6z0UmBt6YT4,18432
|
|
200
|
+
rxiv_maker-1.19.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
201
|
+
rxiv_maker-1.19.0.dist-info/entry_points.txt,sha256=ghCN0hI9A1GlG7QY5F6E-xYPflA8CyS4B6bTQ1YLop0,97
|
|
202
|
+
rxiv_maker-1.19.0.dist-info/licenses/LICENSE,sha256=GSZFoPIhWDNJEtSHTQ5dnELN38zFwRiQO2antBezGQk,1093
|
|
203
|
+
rxiv_maker-1.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|