rxiv-maker 1.15.9__py3-none-any.whl → 1.16.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/config/defaults.py +2 -0
- rxiv_maker/config/validator.py +4 -0
- rxiv_maker/converters/citation_processor.py +19 -6
- rxiv_maker/core/managers/config_manager.py +1 -0
- rxiv_maker/core/managers/unified_config_manager.py +1 -0
- rxiv_maker/engines/operations/build_manager.py +29 -5
- rxiv_maker/engines/operations/generate_preprint.py +1 -1
- rxiv_maker/exporters/docx_exporter.py +7 -1
- rxiv_maker/exporters/docx_writer.py +14 -5
- rxiv_maker/processors/template_processor.py +40 -5
- rxiv_maker/services/manuscript_service.py +1 -1
- rxiv_maker/templates/registry.py +3 -0
- rxiv_maker/utils/author_name_formatter.py +311 -0
- rxiv_maker/utils/bst_generator.py +119 -0
- rxiv_maker/utils/docx_helpers.py +11 -2
- {rxiv_maker-1.15.9.dist-info → rxiv_maker-1.16.0.dist-info}/METADATA +2 -1
- {rxiv_maker-1.15.9.dist-info → rxiv_maker-1.16.0.dist-info}/RECORD +21 -19
- {rxiv_maker-1.15.9.dist-info → rxiv_maker-1.16.0.dist-info}/WHEEL +0 -0
- {rxiv_maker-1.15.9.dist-info → rxiv_maker-1.16.0.dist-info}/entry_points.txt +0 -0
- {rxiv_maker-1.15.9.dist-info → rxiv_maker-1.16.0.dist-info}/licenses/LICENSE +0 -0
rxiv_maker/__version__.py
CHANGED
rxiv_maker/config/defaults.py
CHANGED
|
@@ -10,6 +10,8 @@ from typing import Any, Dict
|
|
|
10
10
|
DEFAULT_CONFIG: Dict[str, Any] = {
|
|
11
11
|
# Citation configuration
|
|
12
12
|
"citation_style": "numbered", # or "author-date"
|
|
13
|
+
# Bibliography author name format
|
|
14
|
+
"bibliography_author_format": "lastname_firstname", # Options: lastname_initials, lastname_firstname, firstname_lastname
|
|
13
15
|
# Figures configuration
|
|
14
16
|
"figures": {
|
|
15
17
|
"directory": "FIGURES",
|
rxiv_maker/config/validator.py
CHANGED
|
@@ -391,6 +391,10 @@ class ConfigValidator:
|
|
|
391
391
|
},
|
|
392
392
|
"bibliography": {"type": "string"},
|
|
393
393
|
"citation_style": {"type": "string", "enum": ["numbered", "author-date"]},
|
|
394
|
+
"bibliography_author_format": {
|
|
395
|
+
"type": "string",
|
|
396
|
+
"enum": ["lastname_initials", "lastname_firstname", "firstname_lastname"],
|
|
397
|
+
},
|
|
394
398
|
"enable_inline_doi_resolution": {"type": "boolean"},
|
|
395
399
|
"language": {"type": "string", "enum": ["en", "es", "pt", "fr", "de"]},
|
|
396
400
|
"license": {"type": "string"},
|
|
@@ -158,10 +158,10 @@ def process_citations_in_text(text: MarkdownContent, citation_style: str = "numb
|
|
|
158
158
|
# Match email-like patterns: word@word.word or @word.word (domain patterns)
|
|
159
159
|
text = re.sub(r"(\w+@[\w.-]+\.\w+|@[\w.-]+\.\w+)", protect_email, text)
|
|
160
160
|
|
|
161
|
-
# Handle single citations like @citation_key (but not
|
|
161
|
+
# Handle single citations like @citation_key (but not cross-references)
|
|
162
162
|
# Allow alphanumeric, underscore, and hyphen in citation keys
|
|
163
|
-
# Exclude
|
|
164
|
-
text = re.sub(r"@(?!
|
|
163
|
+
# Exclude all cross-references by not matching @word: patterns (@fig:, @sfig:, @snote:, @tbl:, etc.)
|
|
164
|
+
text = re.sub(r"@(?![a-zA-Z]+:)([a-zA-Z0-9_-]+)", rf"\\{inline_cmd}{{\1}}", text)
|
|
165
165
|
|
|
166
166
|
# Restore protected email patterns
|
|
167
167
|
for i, pattern in enumerate(email_patterns):
|
|
@@ -194,16 +194,29 @@ def extract_citations_from_text(text: MarkdownContent) -> list[CitationKey]:
|
|
|
194
194
|
"""
|
|
195
195
|
citations: list[CitationKey] = []
|
|
196
196
|
|
|
197
|
+
# First, remove content inside backticks (inline code) to avoid extracting example citations
|
|
198
|
+
# This prevents `@key` or `@Knuth...` from being treated as actual citations
|
|
199
|
+
backtick_patterns = []
|
|
200
|
+
|
|
201
|
+
def protect_backticks(match):
|
|
202
|
+
backtick_patterns.append(match.group(0))
|
|
203
|
+
return f"__BACKTICK_PATTERN_{len(backtick_patterns) - 1}__"
|
|
204
|
+
|
|
205
|
+
# Match both single backticks `...` and triple backticks ```...```
|
|
206
|
+
text_cleaned = re.sub(r"`[^`]+`", protect_backticks, text)
|
|
207
|
+
text_cleaned = re.sub(r"```.*?```", protect_backticks, text_cleaned, flags=re.DOTALL)
|
|
208
|
+
|
|
197
209
|
# Find bracketed multiple citations
|
|
198
|
-
bracketed_matches = re.findall(r"\[(@[^]]+)\]",
|
|
210
|
+
bracketed_matches = re.findall(r"\[(@[^]]+)\]", text_cleaned)
|
|
199
211
|
for match in bracketed_matches:
|
|
200
212
|
for cite in match.split(";"):
|
|
201
213
|
clean_cite = cite.strip().lstrip("@").strip()
|
|
202
214
|
if clean_cite and clean_cite not in citations:
|
|
203
215
|
citations.append(clean_cite)
|
|
204
216
|
|
|
205
|
-
# Find single citations (excluding
|
|
206
|
-
|
|
217
|
+
# Find single citations (excluding all cross-references like @fig:, @sfig:, @snote:, @tbl:, etc.)
|
|
218
|
+
# Exclude any pattern that looks like @word: (cross-reference format)
|
|
219
|
+
single_matches = re.findall(r"@(?![a-zA-Z]+:)([a-zA-Z0-9_-]+)", text_cleaned)
|
|
207
220
|
for cite in single_matches:
|
|
208
221
|
if cite not in citations:
|
|
209
222
|
citations.append(cite)
|
|
@@ -38,6 +38,7 @@ class ConfigManager:
|
|
|
38
38
|
# Configuration search paths (in order of priority)
|
|
39
39
|
# Only search in manuscript directory, no global home directory configs
|
|
40
40
|
self.config_paths = [
|
|
41
|
+
self.base_dir / "00_CONFIG.yml", # Legacy manuscript config (first priority)
|
|
41
42
|
self.base_dir / "rxiv.yml",
|
|
42
43
|
self.base_dir / "rxiv.yaml",
|
|
43
44
|
self.base_dir / ".rxiv.yml",
|
|
@@ -39,6 +39,7 @@ CONFIG_SCOPE_MAP = {
|
|
|
39
39
|
"output": ConfigScope.MANUSCRIPT,
|
|
40
40
|
"figures": ConfigScope.MANUSCRIPT,
|
|
41
41
|
"bibliography": ConfigScope.MANUSCRIPT,
|
|
42
|
+
"bibliography_author_format": ConfigScope.MANUSCRIPT,
|
|
42
43
|
"validation": ConfigScope.MANUSCRIPT,
|
|
43
44
|
"cache": ConfigScope.MANUSCRIPT,
|
|
44
45
|
"acknowledge_rxiv_maker": ConfigScope.MANUSCRIPT,
|
|
@@ -781,8 +781,30 @@ class BuildManager:
|
|
|
781
781
|
self.log("Copying style files...", "STEP")
|
|
782
782
|
|
|
783
783
|
try:
|
|
784
|
-
#
|
|
785
|
-
|
|
784
|
+
# Generate custom .bst file with author name format preference
|
|
785
|
+
from ...core.managers.config_manager import ConfigManager
|
|
786
|
+
from ...utils.bst_generator import generate_bst_file
|
|
787
|
+
|
|
788
|
+
# Load config to get bibliography author format
|
|
789
|
+
config_manager = ConfigManager(base_dir=self.path_manager.manuscript_path)
|
|
790
|
+
config = config_manager.load_config()
|
|
791
|
+
author_format = config.get("bibliography_author_format", "lastname_firstname")
|
|
792
|
+
|
|
793
|
+
# Generate custom .bst file directly to output directory
|
|
794
|
+
try:
|
|
795
|
+
output_dir = self.path_manager.output_dir
|
|
796
|
+
generate_bst_file(author_format, output_dir)
|
|
797
|
+
self.log(f"Generated custom .bst file with format: {author_format}", "INFO")
|
|
798
|
+
except Exception as e:
|
|
799
|
+
self.log(f"Failed to generate custom .bst file: {e}. Using default.", "WARNING")
|
|
800
|
+
# If generation failed, copy the default .bst
|
|
801
|
+
copied_files = self.path_manager.copy_style_files_to_output()
|
|
802
|
+
for copied_file in copied_files:
|
|
803
|
+
self.log(f"Copied {copied_file.name} to output directory", "INFO")
|
|
804
|
+
return True
|
|
805
|
+
|
|
806
|
+
# Copy only .cls file (not .bst since we generated a custom one)
|
|
807
|
+
copied_files = self.path_manager.copy_style_files_to_output(style_files=["rxiv_maker_style.cls"])
|
|
786
808
|
|
|
787
809
|
for copied_file in copied_files:
|
|
788
810
|
self.log(f"Copied {copied_file.name} to output directory", "INFO")
|
|
@@ -897,10 +919,12 @@ class BuildManager:
|
|
|
897
919
|
verbose=self.verbose,
|
|
898
920
|
)
|
|
899
921
|
success = tracker.generate_change_tracked_pdf()
|
|
900
|
-
|
|
922
|
+
|
|
901
923
|
if self.performance_tracker:
|
|
902
|
-
self.performance_tracker.end_operation(
|
|
903
|
-
|
|
924
|
+
self.performance_tracker.end_operation(
|
|
925
|
+
"complete_build", metadata={"result": "success" if success else "failure"}
|
|
926
|
+
)
|
|
927
|
+
|
|
904
928
|
op.add_metadata("build_successful", success)
|
|
905
929
|
op.add_metadata("track_changes", True)
|
|
906
930
|
return success
|
|
@@ -34,7 +34,7 @@ def generate_preprint(output_dir, yaml_metadata, manuscript_path=None):
|
|
|
34
34
|
manuscript_md = find_manuscript_md(manuscript_path)
|
|
35
35
|
|
|
36
36
|
# Process all template replacements
|
|
37
|
-
template_content = process_template_replacements(template_content, yaml_metadata, str(manuscript_md))
|
|
37
|
+
template_content = process_template_replacements(template_content, yaml_metadata, str(manuscript_md), output_dir)
|
|
38
38
|
|
|
39
39
|
# Extract manuscript name using centralized logic (PathManager handles this via write_manuscript_output)
|
|
40
40
|
# The write_manuscript_output function now uses PathManager internally for consistent name extraction
|
|
@@ -11,6 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
from typing import Any, Dict
|
|
12
12
|
|
|
13
13
|
from ..core.logging_config import get_logger
|
|
14
|
+
from ..core.managers.config_manager import ConfigManager
|
|
14
15
|
from ..core.path_manager import PathManager
|
|
15
16
|
from ..processors.yaml_processor import extract_yaml_metadata
|
|
16
17
|
from ..utils.bibliography_parser import parse_bib_file
|
|
@@ -44,6 +45,11 @@ class DocxExporter:
|
|
|
44
45
|
self.resolve_dois = resolve_dois
|
|
45
46
|
self.include_footnotes = include_footnotes
|
|
46
47
|
|
|
48
|
+
# Load config to get author name format preference
|
|
49
|
+
config_manager = ConfigManager(base_dir=Path(manuscript_path))
|
|
50
|
+
config = config_manager.load_config()
|
|
51
|
+
self.author_format = config.get("bibliography_author_format", "lastname_firstname")
|
|
52
|
+
|
|
47
53
|
# Components
|
|
48
54
|
self.citation_mapper = CitationMapper()
|
|
49
55
|
self.content_processor = DocxContentProcessor()
|
|
@@ -317,7 +323,7 @@ class DocxExporter:
|
|
|
317
323
|
# doi = self._resolve_doi_from_metadata(entry)
|
|
318
324
|
|
|
319
325
|
# Format entry (full format for DOCX bibliography)
|
|
320
|
-
formatted = format_bibliography_entry(entry, doi, slim=False)
|
|
326
|
+
formatted = format_bibliography_entry(entry, doi, slim=False, author_format=self.author_format)
|
|
321
327
|
|
|
322
328
|
bibliography[number] = {"key": key, "entry": entry, "doi": doi, "formatted": formatted}
|
|
323
329
|
|
|
@@ -146,14 +146,23 @@ class DocxWriter:
|
|
|
146
146
|
|
|
147
147
|
# Collect unique affiliations and build mapping
|
|
148
148
|
all_affiliations = []
|
|
149
|
-
affiliation_map = {} # Maps affiliation
|
|
149
|
+
affiliation_map = {} # Maps affiliation shortname to number
|
|
150
|
+
|
|
151
|
+
# Get full affiliation details from metadata
|
|
152
|
+
affiliation_details = {a.get("shortname"): a for a in metadata.get("affiliations", [])}
|
|
150
153
|
|
|
151
154
|
for author in authors:
|
|
152
155
|
author_affils = author.get("affiliations", [])
|
|
153
|
-
for
|
|
154
|
-
if
|
|
155
|
-
affiliation_map[
|
|
156
|
-
|
|
156
|
+
for affil_shortname in author_affils:
|
|
157
|
+
if affil_shortname not in affiliation_map:
|
|
158
|
+
affiliation_map[affil_shortname] = len(affiliation_map) + 1
|
|
159
|
+
# Look up full affiliation info
|
|
160
|
+
affil_info = affiliation_details.get(affil_shortname, {})
|
|
161
|
+
full_name = affil_info.get("full_name", affil_shortname)
|
|
162
|
+
location = affil_info.get("location", "")
|
|
163
|
+
# Format: "Full Name, Location" or just "Full Name" if no location
|
|
164
|
+
affil_text = f"{full_name}, {location}" if location else full_name
|
|
165
|
+
all_affiliations.append(affil_text)
|
|
157
166
|
|
|
158
167
|
# Add authors with superscript affiliation numbers
|
|
159
168
|
if authors:
|
|
@@ -298,8 +298,20 @@ def generate_keywords(yaml_metadata):
|
|
|
298
298
|
return result
|
|
299
299
|
|
|
300
300
|
|
|
301
|
-
def generate_bibliography(yaml_metadata):
|
|
302
|
-
"""Generate LaTeX bibliography section from YAML metadata.
|
|
301
|
+
def generate_bibliography(yaml_metadata, output_dir=None):
|
|
302
|
+
"""Generate LaTeX bibliography section from YAML metadata.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
yaml_metadata: Manuscript metadata dictionary
|
|
306
|
+
output_dir: Output directory for generated files (optional)
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
LaTeX bibliography command string
|
|
310
|
+
"""
|
|
311
|
+
from pathlib import Path
|
|
312
|
+
|
|
313
|
+
from ..utils.bst_generator import generate_bst_file
|
|
314
|
+
|
|
303
315
|
bibliography_config = yaml_metadata.get("bibliography", "03_REFERENCES")
|
|
304
316
|
|
|
305
317
|
# Handle both dict and string formats for backward compatibility
|
|
@@ -312,11 +324,34 @@ def generate_bibliography(yaml_metadata):
|
|
|
312
324
|
if bibliography.endswith(".bib"):
|
|
313
325
|
bibliography = bibliography[:-4]
|
|
314
326
|
|
|
327
|
+
# Generate custom .bst file with author name format preference
|
|
328
|
+
if output_dir:
|
|
329
|
+
author_format = yaml_metadata.get("bibliography_author_format", "lastname_firstname")
|
|
330
|
+
try:
|
|
331
|
+
output_path = Path(output_dir)
|
|
332
|
+
generate_bst_file(author_format, output_path)
|
|
333
|
+
except Exception as e:
|
|
334
|
+
# Log warning but don't fail - fall back to default .bst
|
|
335
|
+
import logging
|
|
336
|
+
|
|
337
|
+
logger = logging.getLogger(__name__)
|
|
338
|
+
logger.warning(f"Failed to generate custom .bst file: {e}. Using default.")
|
|
339
|
+
|
|
315
340
|
return f"\\bibliography{{{bibliography}}}"
|
|
316
341
|
|
|
317
342
|
|
|
318
|
-
def process_template_replacements(template_content, yaml_metadata, article_md):
|
|
319
|
-
"""Process all template replacements with metadata and content.
|
|
343
|
+
def process_template_replacements(template_content, yaml_metadata, article_md, output_dir=None):
|
|
344
|
+
"""Process all template replacements with metadata and content.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
template_content: LaTeX template content
|
|
348
|
+
yaml_metadata: Manuscript metadata dictionary
|
|
349
|
+
article_md: Article markdown content
|
|
350
|
+
output_dir: Output directory for generated files (optional)
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Processed template content with all replacements
|
|
354
|
+
"""
|
|
320
355
|
# Process draft watermark based on status field
|
|
321
356
|
is_draft = False
|
|
322
357
|
if "status" in yaml_metadata:
|
|
@@ -424,7 +459,7 @@ def process_template_replacements(template_content, yaml_metadata, article_md):
|
|
|
424
459
|
template_content = template_content.replace("<PY-RPL:KEYWORDS>", keywords_section)
|
|
425
460
|
|
|
426
461
|
# Generate bibliography section
|
|
427
|
-
bibliography_section = generate_bibliography(yaml_metadata)
|
|
462
|
+
bibliography_section = generate_bibliography(yaml_metadata, output_dir)
|
|
428
463
|
template_content = template_content.replace("<PY-RPL:BIBLIOGRAPHY>", bibliography_section)
|
|
429
464
|
|
|
430
465
|
# Extract content sections from markdown
|
|
@@ -221,7 +221,7 @@ class ManuscriptService(BaseService):
|
|
|
221
221
|
|
|
222
222
|
# Process template with metadata
|
|
223
223
|
processed_content = process_template_replacements(
|
|
224
|
-
template_content, metadata.raw, str(metadata.manuscript_path)
|
|
224
|
+
template_content, metadata.raw, str(metadata.manuscript_path), output_dir
|
|
225
225
|
)
|
|
226
226
|
|
|
227
227
|
# Write output
|
rxiv_maker/templates/registry.py
CHANGED
|
@@ -154,6 +154,9 @@ citation_style: "numbered"
|
|
|
154
154
|
#
|
|
155
155
|
# methods_placement: "after_bibliography" # Options: "inline", "after_results", "after_bibliography"
|
|
156
156
|
# acknowledge_rxiv_maker: true
|
|
157
|
+
#
|
|
158
|
+
# # Bibliography author name format (applies to both PDF and DOCX):
|
|
159
|
+
# bibliography_author_format: "lastname_firstname" # Options: "lastname_initials" (Smith, J.A.), "lastname_firstname" (Smith, John A.), "firstname_lastname" (John A. Smith)
|
|
157
160
|
"""
|
|
158
161
|
|
|
159
162
|
def _get_default_main_template(self) -> str:
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Author name parsing and formatting utilities.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to parse, format, and transform author names
|
|
4
|
+
between different bibliographic citation formats.
|
|
5
|
+
|
|
6
|
+
Supported formats:
|
|
7
|
+
- lastname_initials: "Smith, J.A."
|
|
8
|
+
- lastname_firstname: "Smith, John A."
|
|
9
|
+
- firstname_lastname: "John A. Smith"
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from typing import Dict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def extract_initials(given_name: str) -> str:
|
|
17
|
+
"""Extract initials from a given name.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
given_name: Given name(s), which may include first and middle names
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Formatted initials with periods
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
>>> extract_initials("John Alan")
|
|
27
|
+
'J.A.'
|
|
28
|
+
>>> extract_initials("J. A.")
|
|
29
|
+
'J.A.'
|
|
30
|
+
>>> extract_initials("Jean-Paul")
|
|
31
|
+
'J.-P.'
|
|
32
|
+
>>> extract_initials("John")
|
|
33
|
+
'J.'
|
|
34
|
+
"""
|
|
35
|
+
if not given_name or not given_name.strip():
|
|
36
|
+
return ""
|
|
37
|
+
|
|
38
|
+
given_name = given_name.strip()
|
|
39
|
+
|
|
40
|
+
# Handle hyphenated names specially (e.g., "Jean-Paul" → "J.-P.")
|
|
41
|
+
if "-" in given_name:
|
|
42
|
+
# Remove periods first, then split on hyphen
|
|
43
|
+
cleaned = given_name.replace(".", "").strip()
|
|
44
|
+
parts = cleaned.split("-")
|
|
45
|
+
initials = "-".join(part[0].upper() + "." for part in parts if part and part.strip())
|
|
46
|
+
return initials
|
|
47
|
+
|
|
48
|
+
# Remove periods and split on whitespace to handle multi-word names
|
|
49
|
+
cleaned = given_name.replace(".", "").strip()
|
|
50
|
+
words = cleaned.split()
|
|
51
|
+
|
|
52
|
+
if not words:
|
|
53
|
+
return ""
|
|
54
|
+
|
|
55
|
+
# Handle concatenated initials like "JA" → "J.A."
|
|
56
|
+
# If we have a single "word" with multiple uppercase letters, treat each as an initial
|
|
57
|
+
if len(words) == 1 and len(words[0]) > 1:
|
|
58
|
+
word = words[0]
|
|
59
|
+
# Check if it looks like concatenated initials (all or mostly uppercase)
|
|
60
|
+
uppercase_count = sum(1 for c in word if c.isupper())
|
|
61
|
+
if uppercase_count >= len(word) * 0.8: # 80% or more uppercase
|
|
62
|
+
# Treat each uppercase letter as an initial
|
|
63
|
+
initials = [c.upper() + "." for c in word if c.isupper()]
|
|
64
|
+
return "".join(initials) if initials else ""
|
|
65
|
+
|
|
66
|
+
# Extract first letter from each word
|
|
67
|
+
initials = []
|
|
68
|
+
for word in words:
|
|
69
|
+
if word and word[0].isalpha():
|
|
70
|
+
initials.append(word[0].upper() + ".")
|
|
71
|
+
|
|
72
|
+
return "".join(initials) if initials else ""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def parse_author_name(name_str: str) -> Dict[str, str]:
|
|
76
|
+
"""Parse an author name into components.
|
|
77
|
+
|
|
78
|
+
Handles both "LastName, FirstName MiddleName" and "FirstName MiddleName LastName" formats.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
name_str: Author name string to parse
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Dictionary with keys: first, middle, last, suffix, von
|
|
85
|
+
Empty strings for missing components
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
>>> parse_author_name("Smith, John A.")
|
|
89
|
+
{'first': 'John', 'middle': 'A.', 'last': 'Smith', 'suffix': '', 'von': ''}
|
|
90
|
+
>>> parse_author_name("von Neumann, John")
|
|
91
|
+
{'first': 'John', 'middle': '', 'last': 'von Neumann', 'suffix': '', 'von': 'von'}
|
|
92
|
+
>>> parse_author_name("Martin, James Jr.")
|
|
93
|
+
{'first': 'James', 'middle': '', 'last': 'Martin', 'suffix': 'Jr.', 'von': ''}
|
|
94
|
+
>>> parse_author_name("John A. Smith")
|
|
95
|
+
{'first': 'John', 'middle': 'A.', 'last': 'Smith', 'suffix': '', 'von': ''}
|
|
96
|
+
"""
|
|
97
|
+
if not name_str or not name_str.strip():
|
|
98
|
+
return {"first": "", "middle": "", "last": "", "suffix": "", "von": ""}
|
|
99
|
+
|
|
100
|
+
name_str = name_str.strip()
|
|
101
|
+
|
|
102
|
+
# Common suffixes to detect
|
|
103
|
+
suffixes = ["Jr.", "Jr", "Sr.", "Sr", "II", "III", "IV", "V"]
|
|
104
|
+
|
|
105
|
+
# Check for "LastName, FirstName" format (comma indicates this format)
|
|
106
|
+
if "," in name_str:
|
|
107
|
+
# Split by comma
|
|
108
|
+
parts = [p.strip() for p in name_str.split(",", 1)]
|
|
109
|
+
last_part = parts[0]
|
|
110
|
+
given_part = parts[1] if len(parts) > 1 else ""
|
|
111
|
+
|
|
112
|
+
# Check if last part of given_part is a suffix
|
|
113
|
+
suffix = ""
|
|
114
|
+
if given_part:
|
|
115
|
+
given_words = given_part.split()
|
|
116
|
+
if given_words and given_words[-1] in suffixes:
|
|
117
|
+
suffix = given_words[-1]
|
|
118
|
+
given_part = " ".join(given_words[:-1])
|
|
119
|
+
|
|
120
|
+
# Parse given names (first + middle)
|
|
121
|
+
given_words = given_part.split() if given_part else []
|
|
122
|
+
first = given_words[0] if given_words else ""
|
|
123
|
+
middle = " ".join(given_words[1:]) if len(given_words) > 1 else ""
|
|
124
|
+
|
|
125
|
+
# Check for von/van prefix in last name
|
|
126
|
+
von = ""
|
|
127
|
+
last_words = last_part.split()
|
|
128
|
+
if last_words and last_words[0].lower() in ["von", "van", "de", "del", "della", "di"]:
|
|
129
|
+
von = last_words[0]
|
|
130
|
+
# Keep von as part of last name
|
|
131
|
+
last = last_part
|
|
132
|
+
else:
|
|
133
|
+
last = last_part
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
"first": first,
|
|
137
|
+
"middle": middle,
|
|
138
|
+
"last": last,
|
|
139
|
+
"suffix": suffix,
|
|
140
|
+
"von": von,
|
|
141
|
+
}
|
|
142
|
+
else:
|
|
143
|
+
# "FirstName MiddleName LastName" format
|
|
144
|
+
words = name_str.split()
|
|
145
|
+
|
|
146
|
+
if not words:
|
|
147
|
+
return {"first": "", "middle": "", "last": "", "suffix": "", "von": ""}
|
|
148
|
+
|
|
149
|
+
# Check if last word is a suffix
|
|
150
|
+
suffix = ""
|
|
151
|
+
if words[-1] in suffixes:
|
|
152
|
+
suffix = words[-1]
|
|
153
|
+
words = words[:-1]
|
|
154
|
+
|
|
155
|
+
if not words:
|
|
156
|
+
return {"first": "", "middle": "", "last": "", "suffix": suffix, "von": ""}
|
|
157
|
+
|
|
158
|
+
# Single name (e.g., "Plato")
|
|
159
|
+
if len(words) == 1:
|
|
160
|
+
return {
|
|
161
|
+
"first": "",
|
|
162
|
+
"middle": "",
|
|
163
|
+
"last": words[0],
|
|
164
|
+
"suffix": suffix,
|
|
165
|
+
"von": "",
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# Check for von/van prefix
|
|
169
|
+
von = ""
|
|
170
|
+
von_idx = None
|
|
171
|
+
for i, word in enumerate(words[:-1]): # Don't check last word
|
|
172
|
+
if word.lower() in ["von", "van", "de", "del", "della", "di"]:
|
|
173
|
+
von = word
|
|
174
|
+
von_idx = i
|
|
175
|
+
break
|
|
176
|
+
|
|
177
|
+
if von_idx is not None:
|
|
178
|
+
# Everything before von is first/middle, von + rest is last
|
|
179
|
+
first = words[0] if von_idx > 0 else ""
|
|
180
|
+
middle = " ".join(words[1:von_idx]) if von_idx > 1 else ""
|
|
181
|
+
last = " ".join(words[von_idx:])
|
|
182
|
+
else:
|
|
183
|
+
# Standard: First [Middle...] Last
|
|
184
|
+
first = words[0]
|
|
185
|
+
middle = " ".join(words[1:-1]) if len(words) > 2 else ""
|
|
186
|
+
last = words[-1]
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
"first": first,
|
|
190
|
+
"middle": middle,
|
|
191
|
+
"last": last,
|
|
192
|
+
"suffix": suffix,
|
|
193
|
+
"von": von,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def format_author_name(author_parts: Dict[str, str], format_type: str) -> str:
|
|
198
|
+
"""Format an author name according to the specified format.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
author_parts: Dictionary with author name components (from parse_author_name)
|
|
202
|
+
format_type: One of "lastname_initials", "lastname_firstname", "firstname_lastname"
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Formatted author name string
|
|
206
|
+
|
|
207
|
+
Examples:
|
|
208
|
+
>>> parts = {'first': 'John', 'middle': 'A.', 'last': 'Smith', 'suffix': '', 'von': ''}
|
|
209
|
+
>>> format_author_name(parts, "lastname_initials")
|
|
210
|
+
'Smith, J.A.'
|
|
211
|
+
>>> format_author_name(parts, "lastname_firstname")
|
|
212
|
+
'Smith, John A.'
|
|
213
|
+
>>> format_author_name(parts, "firstname_lastname")
|
|
214
|
+
'John A. Smith'
|
|
215
|
+
"""
|
|
216
|
+
first = author_parts.get("first", "").strip()
|
|
217
|
+
middle = author_parts.get("middle", "").strip()
|
|
218
|
+
last = author_parts.get("last", "").strip()
|
|
219
|
+
suffix = author_parts.get("suffix", "").strip()
|
|
220
|
+
|
|
221
|
+
# Build given name (first + middle)
|
|
222
|
+
given_parts = []
|
|
223
|
+
if first:
|
|
224
|
+
given_parts.append(first)
|
|
225
|
+
if middle:
|
|
226
|
+
given_parts.append(middle)
|
|
227
|
+
given_name = " ".join(given_parts)
|
|
228
|
+
|
|
229
|
+
# Handle single-name authors (e.g., "Plato")
|
|
230
|
+
if not given_name and last:
|
|
231
|
+
return last
|
|
232
|
+
|
|
233
|
+
if not last:
|
|
234
|
+
return given_name # Fallback if no last name
|
|
235
|
+
|
|
236
|
+
# Format based on type
|
|
237
|
+
if format_type == "lastname_initials":
|
|
238
|
+
# Extract initials from given name
|
|
239
|
+
initials = extract_initials(given_name)
|
|
240
|
+
if initials:
|
|
241
|
+
result = f"{last}, {initials}"
|
|
242
|
+
else:
|
|
243
|
+
result = last
|
|
244
|
+
elif format_type == "lastname_firstname":
|
|
245
|
+
if given_name:
|
|
246
|
+
result = f"{last}, {given_name}"
|
|
247
|
+
else:
|
|
248
|
+
result = last
|
|
249
|
+
elif format_type == "firstname_lastname":
|
|
250
|
+
if given_name:
|
|
251
|
+
result = f"{given_name} {last}"
|
|
252
|
+
else:
|
|
253
|
+
result = last
|
|
254
|
+
else:
|
|
255
|
+
# Default to lastname_firstname
|
|
256
|
+
if given_name:
|
|
257
|
+
result = f"{last}, {given_name}"
|
|
258
|
+
else:
|
|
259
|
+
result = last
|
|
260
|
+
|
|
261
|
+
# Add suffix if present
|
|
262
|
+
if suffix:
|
|
263
|
+
if format_type in ["lastname_initials", "lastname_firstname"]:
|
|
264
|
+
result = f"{result}, {suffix}"
|
|
265
|
+
else:
|
|
266
|
+
result = f"{result} {suffix}"
|
|
267
|
+
|
|
268
|
+
return result
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def format_author_list(authors_str: str, format_type: str) -> str:
|
|
272
|
+
"""Format a list of authors separated by 'and'.
|
|
273
|
+
|
|
274
|
+
Note: Caller should clean LaTeX commands from authors_str before calling this function.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
authors_str: String of authors separated by " and " (should be LaTeX-cleaned)
|
|
278
|
+
format_type: One of "lastname_initials", "lastname_firstname", "firstname_lastname"
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Formatted author list joined by " and "
|
|
282
|
+
|
|
283
|
+
Examples:
|
|
284
|
+
>>> format_author_list("Smith, John and Jones, Mary A.", "lastname_initials")
|
|
285
|
+
'Smith, J. and Jones, M.A.'
|
|
286
|
+
>>> format_author_list("Smith, John A. and Jones, Mary", "firstname_lastname")
|
|
287
|
+
'John A. Smith and Mary Jones'
|
|
288
|
+
"""
|
|
289
|
+
if not authors_str or not authors_str.strip():
|
|
290
|
+
return ""
|
|
291
|
+
|
|
292
|
+
# Split by " and " (BibTeX standard separator)
|
|
293
|
+
# Handle potential variations: "and", " and ", " and "
|
|
294
|
+
author_list = re.split(r"\s+and\s+", authors_str)
|
|
295
|
+
|
|
296
|
+
# Parse and format each author
|
|
297
|
+
formatted_authors = []
|
|
298
|
+
for author in author_list:
|
|
299
|
+
author = author.strip()
|
|
300
|
+
if not author:
|
|
301
|
+
continue
|
|
302
|
+
|
|
303
|
+
# Parse the name
|
|
304
|
+
author_parts = parse_author_name(author)
|
|
305
|
+
|
|
306
|
+
# Format according to specified type
|
|
307
|
+
formatted = format_author_name(author_parts, format_type)
|
|
308
|
+
formatted_authors.append(formatted)
|
|
309
|
+
|
|
310
|
+
# Join with " and "
|
|
311
|
+
return " and ".join(formatted_authors)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""BibTeX style file generator for custom author name formatting.
|
|
2
|
+
|
|
3
|
+
This module generates custom .bst files with different author name format strings
|
|
4
|
+
to support lastname_initials, lastname_firstname, and firstname_lastname formats.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict
|
|
10
|
+
|
|
11
|
+
from ..core.logging_config import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger()
|
|
14
|
+
|
|
15
|
+
# BibTeX format string mapping
|
|
16
|
+
# BibTeX format codes:
|
|
17
|
+
# ff = full first name, f = first initial
|
|
18
|
+
# vv = von part, ll = last name, jj = junior part
|
|
19
|
+
BST_FORMAT_MAP: Dict[str, str] = {
|
|
20
|
+
"lastname_initials": "{vv~}{ll}{, f.}", # Smith, J.A.
|
|
21
|
+
"lastname_firstname": "{ff~}{vv~}{ll}{, jj}", # Smith, John A. (current default)
|
|
22
|
+
"firstname_lastname": "{ff~}{vv~}{ll}", # John A. Smith
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def generate_bst_file(format_type: str, output_dir: Path) -> Path:
|
|
27
|
+
"""Generate a custom .bst file with the specified author name format.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
format_type: One of "lastname_initials", "lastname_firstname", "firstname_lastname"
|
|
31
|
+
output_dir: Directory where the generated .bst file should be written
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Path to the generated .bst file
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
ValueError: If format_type is not recognized
|
|
38
|
+
FileNotFoundError: If template .bst file cannot be found
|
|
39
|
+
IOError: If .bst file cannot be written
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> output_path = generate_bst_file("lastname_initials", Path("./output"))
|
|
43
|
+
>>> # Creates ./output/rxiv_maker_style.bst with lastname, initials format
|
|
44
|
+
"""
|
|
45
|
+
if format_type not in BST_FORMAT_MAP:
|
|
46
|
+
valid_formats = ", ".join(BST_FORMAT_MAP.keys())
|
|
47
|
+
raise ValueError(f"Invalid format_type '{format_type}'. Must be one of: {valid_formats}")
|
|
48
|
+
|
|
49
|
+
# Get the format string for this format type
|
|
50
|
+
format_string = BST_FORMAT_MAP[format_type]
|
|
51
|
+
|
|
52
|
+
# Find the template .bst file in the package
|
|
53
|
+
# The template is located at src/tex/style/rxiv_maker_style.bst
|
|
54
|
+
package_root = Path(__file__).parent.parent.parent
|
|
55
|
+
template_path = package_root / "tex" / "style" / "rxiv_maker_style.bst"
|
|
56
|
+
|
|
57
|
+
if not template_path.exists():
|
|
58
|
+
raise FileNotFoundError(f"Template .bst file not found at: {template_path}")
|
|
59
|
+
|
|
60
|
+
# Read the template file
|
|
61
|
+
try:
|
|
62
|
+
with open(template_path, "r", encoding="utf-8") as f:
|
|
63
|
+
bst_content = f.read()
|
|
64
|
+
except IOError as e:
|
|
65
|
+
raise IOError(f"Failed to read template .bst file: {e}") from e
|
|
66
|
+
|
|
67
|
+
# Replace the format string on line 222
|
|
68
|
+
# The line looks like: s nameptr "{ff~}{vv~}{ll}{, jj}" format.name$ 't :=
|
|
69
|
+
# We need to replace the format string in quotes
|
|
70
|
+
pattern = r'(s\s+nameptr\s+")([^"]+)("\s+format\.name\$)'
|
|
71
|
+
replacement = rf"\1{format_string}\3"
|
|
72
|
+
|
|
73
|
+
modified_content, num_subs = re.subn(pattern, replacement, bst_content)
|
|
74
|
+
|
|
75
|
+
if num_subs == 0:
|
|
76
|
+
logger.warning("No format string pattern found in .bst file. The .bst file may have been modified.")
|
|
77
|
+
# Still write the file but log a warning
|
|
78
|
+
elif num_subs > 1:
|
|
79
|
+
logger.warning(
|
|
80
|
+
f"Found {num_subs} format string patterns in .bst file. Expected only 1. All have been replaced."
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Create output directory if it doesn't exist
|
|
84
|
+
output_dir = Path(output_dir)
|
|
85
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
# Write the generated .bst file
|
|
88
|
+
output_path = output_dir / "rxiv_maker_style.bst"
|
|
89
|
+
try:
|
|
90
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
91
|
+
f.write(modified_content)
|
|
92
|
+
except IOError as e:
|
|
93
|
+
raise IOError(f"Failed to write generated .bst file: {e}") from e
|
|
94
|
+
|
|
95
|
+
logger.debug(f"Generated .bst file with format '{format_type}' at: {output_path}")
|
|
96
|
+
|
|
97
|
+
return output_path
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_bst_format_string(format_type: str) -> str:
|
|
101
|
+
"""Get the BibTeX format string for a given format type.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
format_type: One of "lastname_initials", "lastname_firstname", "firstname_lastname"
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
BibTeX format string
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
ValueError: If format_type is not recognized
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
>>> get_bst_format_string("lastname_initials")
|
|
114
|
+
'{vv~}{ll}{, f.}'
|
|
115
|
+
"""
|
|
116
|
+
if format_type not in BST_FORMAT_MAP:
|
|
117
|
+
valid_formats = ", ".join(BST_FORMAT_MAP.keys())
|
|
118
|
+
raise ValueError(f"Invalid format_type '{format_type}'. Must be one of: {valid_formats}")
|
|
119
|
+
return BST_FORMAT_MAP[format_type]
|
rxiv_maker/utils/docx_helpers.py
CHANGED
|
@@ -14,6 +14,8 @@ from typing import Optional
|
|
|
14
14
|
|
|
15
15
|
from PIL import Image
|
|
16
16
|
|
|
17
|
+
from rxiv_maker.utils.author_name_formatter import format_author_list
|
|
18
|
+
|
|
17
19
|
from ..utils.bibliography_parser import BibEntry
|
|
18
20
|
|
|
19
21
|
|
|
@@ -39,7 +41,12 @@ def remove_yaml_header(content: str) -> str:
|
|
|
39
41
|
return content
|
|
40
42
|
|
|
41
43
|
|
|
42
|
-
def format_bibliography_entry(
|
|
44
|
+
def format_bibliography_entry(
|
|
45
|
+
entry: BibEntry,
|
|
46
|
+
doi: Optional[str] = None,
|
|
47
|
+
slim: bool = False,
|
|
48
|
+
author_format: str = "lastname_firstname",
|
|
49
|
+
) -> str:
|
|
43
50
|
"""Format a bibliography entry for display.
|
|
44
51
|
|
|
45
52
|
Full format: Author (Year). Title. Journal Volume(Number): Pages. DOI (if provided)
|
|
@@ -50,6 +57,7 @@ def format_bibliography_entry(entry: BibEntry, doi: Optional[str] = None, slim:
|
|
|
50
57
|
entry: Bibliography entry to format
|
|
51
58
|
doi: DOI string (optional)
|
|
52
59
|
slim: If True, use slim format (LastName, Year)
|
|
60
|
+
author_format: Format for author names - "lastname_initials", "lastname_firstname", "firstname_lastname"
|
|
53
61
|
|
|
54
62
|
Returns:
|
|
55
63
|
Formatted bibliography string
|
|
@@ -69,8 +77,9 @@ def format_bibliography_entry(entry: BibEntry, doi: Optional[str] = None, slim:
|
|
|
69
77
|
author = entry.fields.get("author", "Unknown Author")
|
|
70
78
|
year = entry.fields.get("year", "n.d.")
|
|
71
79
|
|
|
72
|
-
# Clean LaTeX commands from author names
|
|
80
|
+
# Clean LaTeX commands from author names and format according to specified style
|
|
73
81
|
author = clean_latex_commands(author)
|
|
82
|
+
author = format_author_list(author, author_format)
|
|
74
83
|
|
|
75
84
|
if slim:
|
|
76
85
|
# Slim format: First author last name, Year
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rxiv-maker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.16.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
|
|
@@ -201,6 +201,7 @@ rxiv pdf
|
|
|
201
201
|
- BibTeX integration with `[@citation]` syntax
|
|
202
202
|
- Automatic bibliography generation
|
|
203
203
|
- **Multiple citation styles**: Choose between numbered `[1, 2]` or author-date `(Smith, 2024)` citations
|
|
204
|
+
- **Configurable author name formatting**: Format bibliography as `Smith, J.A.` or `Smith, John A.` or `John A. Smith`
|
|
204
205
|
- **Inline DOI resolution**: Paste DOIs directly in text `10.1038/...` and auto-convert to citations
|
|
205
206
|
- CrossRef/DataCite DOI validation and metadata fetching
|
|
206
207
|
|
|
@@ -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=0b3_5IT4Rl1KTRqfSivXOu2oAnVS0q_ptEV7xe5L9Yo,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
|
|
@@ -40,10 +40,10 @@ rxiv_maker/cli/framework/content_commands.py,sha256=RilxKeG2c1m2fu0CtWAvP3cGh11D
|
|
|
40
40
|
rxiv_maker/cli/framework/decorators.py,sha256=fh085e3k1CaLSMoZevt8hvgnEuejrf-mcNS-dwXoY_A,10365
|
|
41
41
|
rxiv_maker/cli/framework/utility_commands.py,sha256=_P_KwjlyiNS-vVboU-DqkHynGPqzXyoZRWGmLMLTnOs,26214
|
|
42
42
|
rxiv_maker/cli/framework/workflow_commands.py,sha256=GbK1Wv4cTQfCiOyukn_R3q3IUqKvLk8MMK1vOXW3PaE,30556
|
|
43
|
-
rxiv_maker/config/defaults.py,sha256=
|
|
44
|
-
rxiv_maker/config/validator.py,sha256=
|
|
43
|
+
rxiv_maker/config/defaults.py,sha256=vHyLGVxe5-z9TLxu5f6NhquPvqQkER_KZv_j1I4_dHQ,3055
|
|
44
|
+
rxiv_maker/config/validator.py,sha256=9XDPfo_YgasGt6NLkl6HIhaGh1fr6XsFNiXU2DSsivw,38299
|
|
45
45
|
rxiv_maker/converters/__init__.py,sha256=d7WGsRwWqRQWO117IkKDP0Ap0ERiK0N2-dXHInye3_A,685
|
|
46
|
-
rxiv_maker/converters/citation_processor.py,sha256=
|
|
46
|
+
rxiv_maker/converters/citation_processor.py,sha256=Y4JpSvoqYU4GnqYC5bnB0Zn0OMTAR_FG3G8hWkgQj0o,8917
|
|
47
47
|
rxiv_maker/converters/code_processor.py,sha256=ZFkJsqJ4nYUiDGtLeV7yWgOWUZNUXIFaBxZOPq88UrU,9324
|
|
48
48
|
rxiv_maker/converters/comment_processor.py,sha256=Tlem4btYqMmfRf5AM5s6ZbB_pZPvkFpACz8XsLNVl50,7845
|
|
49
49
|
rxiv_maker/converters/custom_command_processor.py,sha256=89wkIKP2It89qhXPBxiK-ib3Wwb5bKYsVf8E6BBRTlk,23467
|
|
@@ -79,25 +79,25 @@ rxiv_maker/core/cache/doi_cache.py,sha256=lw_ouHlfKNzg4zDMAOEUDhsFLfm88rWcPYBVyj
|
|
|
79
79
|
rxiv_maker/core/cache/secure_cache_utils.py,sha256=EejPWvxw_mUPqO0TRBHYYTsLXWZEUH1qykEfBwgpkcc,18000
|
|
80
80
|
rxiv_maker/core/managers/__init__.py,sha256=sh4ZuZH4YrAu4XTiN9ky1-tQQASKiSTY0udJJAzDRcU,950
|
|
81
81
|
rxiv_maker/core/managers/cache_manager.py,sha256=8btUaRDYPOrUynHWBMz7RVeS9xcpUoyhemFNUi8NqpQ,21893
|
|
82
|
-
rxiv_maker/core/managers/config_manager.py,sha256=
|
|
82
|
+
rxiv_maker/core/managers/config_manager.py,sha256=DdM-3_mfrjHFkPQ2vOmpQnXaoSJU-ianVwUvCEIwZss,18829
|
|
83
83
|
rxiv_maker/core/managers/dependency_manager.py,sha256=DwVRcvk4JcZGzYc-K6ue7aGfw8fSnne50pPI_8Arlzw,25800
|
|
84
84
|
rxiv_maker/core/managers/execution_manager.py,sha256=cEDS0KyWBHf_N74Fc8MC4VRXxmEcaz_DJtyWO5-o628,29585
|
|
85
85
|
rxiv_maker/core/managers/file_manager.py,sha256=SVRnP1JQoGCAms3E7iSpOp_RG60P36Qk9HGAmJDaFvE,18641
|
|
86
86
|
rxiv_maker/core/managers/install_manager.py,sha256=8HChOfbm5-uXsksKMDqmgrYNMJJtyDoL2RDNpbFwhwc,15533
|
|
87
87
|
rxiv_maker/core/managers/state_manager.py,sha256=vqcg5aiAh9NvUJDiV7wTgU2EIGS-qAgZ50n4Xcuo49A,17713
|
|
88
|
-
rxiv_maker/core/managers/unified_config_manager.py,sha256=
|
|
88
|
+
rxiv_maker/core/managers/unified_config_manager.py,sha256=fqoL9-qwixdBOw4kZ3iFZ06nG9nxkUl-Ckc8hxYwxmo,12587
|
|
89
89
|
rxiv_maker/core/managers/validation_manager.py,sha256=wqmYJUybgAX5dJRe6S9lG7tShLWjYOmARIhR0xygHVM,29948
|
|
90
90
|
rxiv_maker/core/managers/workflow_manager.py,sha256=WJehwdVZpaWgjk0SbvRb5AdHIK_b5FQTKFF12bv90HY,27082
|
|
91
91
|
rxiv_maker/data/tips.yaml,sha256=PZvLH4L-keJko1BOvnP_aQpvNVOQ20t6OcPLF1vTWzg,4739
|
|
92
92
|
rxiv_maker/engines/__init__.py,sha256=FiVpmWADcXzP2w_tQk9E4KEABGtsoXniPE7TFk-mwkY,1075
|
|
93
93
|
rxiv_maker/engines/operations/__init__.py,sha256=m0es7aH_qVqAGk6CLS6SMo3ZNL2F_gLrM_wLY1shDR4,1292
|
|
94
94
|
rxiv_maker/engines/operations/add_bibliography.py,sha256=eIYbZRmRFHPm09svhnrsgIjjzt3Imc1U3h4eeEj-D1M,20511
|
|
95
|
-
rxiv_maker/engines/operations/build_manager.py,sha256=
|
|
95
|
+
rxiv_maker/engines/operations/build_manager.py,sha256=TAX4-r8HjraAzzvQuIt0CNlvWLo5pF40I7BRuIxZZRc,45022
|
|
96
96
|
rxiv_maker/engines/operations/cleanup.py,sha256=RfbXif0neEVMlprFIHWyvQh6kwghalcesY3t-69Iwsw,18095
|
|
97
97
|
rxiv_maker/engines/operations/fix_bibliography.py,sha256=ZD8uO4YCxDCMWH4WtBSDc4TOMgM383fcLgsCCW0yK60,22428
|
|
98
98
|
rxiv_maker/engines/operations/generate_docs.py,sha256=f9IjZD2_nK-kD8RLRugRuXMslDgx4857f3AqLGRJFrY,11084
|
|
99
99
|
rxiv_maker/engines/operations/generate_figures.py,sha256=3oIuS0wryO9WpPZ3UD2qm0YscNidTOEiO4_Jd6r3SmY,32842
|
|
100
|
-
rxiv_maker/engines/operations/generate_preprint.py,sha256=
|
|
100
|
+
rxiv_maker/engines/operations/generate_preprint.py,sha256=wpKDAu2RLJ4amSdhX5GZ7hU-iTsTRt4etcEA7AZYp04,2662
|
|
101
101
|
rxiv_maker/engines/operations/prepare_arxiv.py,sha256=cd0JN5IO-Wy9T8ab75eibyaA8_K8Gpwrz2F-95OMnx4,21551
|
|
102
102
|
rxiv_maker/engines/operations/setup_environment.py,sha256=gERuThHTldH0YqgXn85995deHBP6csY1ZhCNgU6-vFg,12691
|
|
103
103
|
rxiv_maker/engines/operations/track_changes.py,sha256=XMU1x31nwLIwbi1lwYJn333O7Tx9llQfBv8yY9O_Dww,24734
|
|
@@ -106,8 +106,8 @@ rxiv_maker/engines/operations/validate_pdf.py,sha256=qyrtL752Uap3i6ntQheY570soVj
|
|
|
106
106
|
rxiv_maker/exporters/__init__.py,sha256=NcTD1SDb8tTgsHhCS1A7TVEZncyWbDRTa6sJIdLqcsE,350
|
|
107
107
|
rxiv_maker/exporters/docx_citation_mapper.py,sha256=Qp3IEqrR6lQGQPQ4JeGdOCWeg97XBbVCKdX1gtX9XfY,4584
|
|
108
108
|
rxiv_maker/exporters/docx_content_processor.py,sha256=3srXfq4lC_FJpTAfu-WB930WheSm6rCy-vNUtFlsflY,23878
|
|
109
|
-
rxiv_maker/exporters/docx_exporter.py,sha256=
|
|
110
|
-
rxiv_maker/exporters/docx_writer.py,sha256=
|
|
109
|
+
rxiv_maker/exporters/docx_exporter.py,sha256=i2G_AF-Co4AOD6ZdRzDZ7VgcJLdCmxu-Fn3SykvBywQ,15152
|
|
110
|
+
rxiv_maker/exporters/docx_writer.py,sha256=qI_0JklqAxF_TUHxO3wPopZpJ44O2qYjMPOptrnU0XM,41915
|
|
111
111
|
rxiv_maker/install/__init__.py,sha256=kAB6P-12IKg_K1MQ-uzeC5IR11O2cNxj0t_2JMhooZs,590
|
|
112
112
|
rxiv_maker/install/dependency_handlers/__init__.py,sha256=NN9dP1usXpYgLpSw0uEnJ6ugX2zefihVjdyDdm1k-cE,231
|
|
113
113
|
rxiv_maker/install/dependency_handlers/latex.py,sha256=xopSJxYkg3D63rH7RoVLN-Ykl87AZqhlUrrG3m6LoWo,3304
|
|
@@ -127,7 +127,7 @@ rxiv_maker/manuscript_utils/figure_utils.py,sha256=FsNtMiA1IOHeA6gQsENmAWLZSAvPp
|
|
|
127
127
|
rxiv_maker/processors/__init__.py,sha256=8UmaeFkbPfcwAL5MnhN2DcOA2k8iuJu0B5dDA6_pmnA,720
|
|
128
128
|
rxiv_maker/processors/author_processor.py,sha256=jC4qZ9M_GADelkp1v8pk46ESUf4DNraXLfMYGhn_7ZM,10016
|
|
129
129
|
rxiv_maker/processors/markdown_preprocessor.py,sha256=Ez_2lhSFEdJY0CS2SKJJESRfnmSdMq4s0jP2nlFSKN8,11228
|
|
130
|
-
rxiv_maker/processors/template_processor.py,sha256=
|
|
130
|
+
rxiv_maker/processors/template_processor.py,sha256=mX8L19Evo9c8DvcdvSdtFHGaGGrQseVsF1CKfj_WYow,30024
|
|
131
131
|
rxiv_maker/processors/yaml_processor.py,sha256=SSXHpwY1Cw81IDN4x40UFtosz-T9l29oh-CZpusmlYo,11499
|
|
132
132
|
rxiv_maker/scripts/__init__.py,sha256=nKsDmj6UAc90uC6sKd-V6O80OMYjAl4Uha9zF_6ayX0,154
|
|
133
133
|
rxiv_maker/scripts/custom_doc_generator.py,sha256=pMkdTI8a4qG8Z3gFUJtPwRuu2i0ioW3pKfs22es0KCk,6311
|
|
@@ -136,20 +136,22 @@ rxiv_maker/services/__init__.py,sha256=8PtucdpuVrO6OmzvW4AyaWXKlQpdErIDeCLlXpKEM
|
|
|
136
136
|
rxiv_maker/services/base.py,sha256=iCfX8yGyCFTNIQf4tPblQzlc4-LFMv-H7p9I3Qc2LT0,5484
|
|
137
137
|
rxiv_maker/services/build_service.py,sha256=0irCnWoYw-0tagwNiNxAwDxlqgS_qpOvSCpc5EJ85I0,2120
|
|
138
138
|
rxiv_maker/services/configuration_service.py,sha256=MCKWFLkMA9hi7wQE5xPpH6KxO6QiWpogrl1ivFRL00o,3899
|
|
139
|
-
rxiv_maker/services/manuscript_service.py,sha256=
|
|
139
|
+
rxiv_maker/services/manuscript_service.py,sha256=wev9gBTbKbcypQMmoKvR7gh2Ha1nRuvRdkrFJcmr3Io,11304
|
|
140
140
|
rxiv_maker/services/publication_service.py,sha256=0p8yQ1jrY3RHwCkzTEl_sAbWYTafRk-NaTqHWZW3_dg,1740
|
|
141
141
|
rxiv_maker/services/validation_service.py,sha256=eWg14NqJu6LzyJBgeXkTaVZAlX4wYFX8ZEvSR5hMx7U,14619
|
|
142
142
|
rxiv_maker/templates/__init__.py,sha256=UTet1pYPkPdgvrLw-wwaY-PAgdjGJasAi_hdyIh0J8s,562
|
|
143
143
|
rxiv_maker/templates/manager.py,sha256=HlI7Qb52866Okf4k1aRh0fUy9heOSNGjMQJtrCdL3Xk,6131
|
|
144
|
-
rxiv_maker/templates/registry.py,sha256=
|
|
144
|
+
rxiv_maker/templates/registry.py,sha256=L8M7M9Yhf2P0SQEghmCXufWfsGQhaW1TYjdkUw-wvDw,12993
|
|
145
145
|
rxiv_maker/tex/python_execution_section.tex,sha256=pHz6NGfZN4ViBo6rInUO5FAuk81sV_Ppqszrvl00w_4,2218
|
|
146
146
|
rxiv_maker/utils/__init__.py,sha256=4ya5VR8jqRqUChlnUeMeeetOuWV-gIvjPwcE1u_1OnI,1540
|
|
147
|
+
rxiv_maker/utils/author_name_formatter.py,sha256=UjvarbyQm89EUIYqckygx3g37o-EcNyvipBtY8GJDxs,10222
|
|
147
148
|
rxiv_maker/utils/bibliography_checksum.py,sha256=Jh4VILSpGQ5KJ9UBCUb7oFy6lZ9_ncXD87vEXxw5jbY,10270
|
|
148
149
|
rxiv_maker/utils/bibliography_parser.py,sha256=WZIQoEpVwdbLmbkw9FdkVgoLE5GX7itqnzPnEEb_fFU,6846
|
|
150
|
+
rxiv_maker/utils/bst_generator.py,sha256=EHNhMT0Tq6YXUh2TfpGwOzYaarAyjEsAeeJ4H4np7Og,4313
|
|
149
151
|
rxiv_maker/utils/changelog_parser.py,sha256=WCDp9Iy6H6_3nC6FB7RLt6i00zuCyvU17sCU4e3pqCY,11954
|
|
150
152
|
rxiv_maker/utils/citation_utils.py,sha256=spIgVxPAN6jPvoG-eOE00rVX_buUGKnUjP1Fhz31sl4,5134
|
|
151
153
|
rxiv_maker/utils/dependency_checker.py,sha256=EdyIvk-W_bhC1DJCpFw5ePhjEU74C9j7RYMm06unBMA,14366
|
|
152
|
-
rxiv_maker/utils/docx_helpers.py,sha256=
|
|
154
|
+
rxiv_maker/utils/docx_helpers.py,sha256=jhxkrU80JjDJXDE3gNfsJt9PqLyByYDfymY_Jm9ZqoI,12860
|
|
153
155
|
rxiv_maker/utils/doi_resolver.py,sha256=8_oy5cTtklm1GCKXpn509yqYsu4P5gYbMjtfQ8dRgFA,10253
|
|
154
156
|
rxiv_maker/utils/email_encoder.py,sha256=QMD5JbGNu68gD8SBdGHfNY8uCgbMzEcmzE1TCYDMgWY,5139
|
|
155
157
|
rxiv_maker/utils/figure_checksum.py,sha256=PWgh2QAErNnnQCV-t-COACQXKICUaggAAIxhgHLCGNM,10748
|
|
@@ -185,8 +187,8 @@ rxiv_maker/validators/doi/metadata_comparator.py,sha256=euqHhKP5sHQAdZbdoAahUn6Y
|
|
|
185
187
|
rxiv_maker/tex/template.tex,sha256=zrJ3aFfu8j9zkg1l375eE9w-j42P3rz16wMD3dSgi1I,1354
|
|
186
188
|
rxiv_maker/tex/style/rxiv_maker_style.bst,sha256=jbVqrJgAm6F88cow5vtZuPBwwmlcYykclTm8RvZIo6Y,24281
|
|
187
189
|
rxiv_maker/tex/style/rxiv_maker_style.cls,sha256=F2qtnS9mI6SwOIaVH76egXZkB2_GzbH4gCTG_ZcfCDQ,24253
|
|
188
|
-
rxiv_maker-1.
|
|
189
|
-
rxiv_maker-1.
|
|
190
|
-
rxiv_maker-1.
|
|
191
|
-
rxiv_maker-1.
|
|
192
|
-
rxiv_maker-1.
|
|
190
|
+
rxiv_maker-1.16.0.dist-info/METADATA,sha256=OM17giwfgx08ZNIEhfHvyQDdT3WqQ5yI3Z1ntj3XLtQ,19775
|
|
191
|
+
rxiv_maker-1.16.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
192
|
+
rxiv_maker-1.16.0.dist-info/entry_points.txt,sha256=ghCN0hI9A1GlG7QY5F6E-xYPflA8CyS4B6bTQ1YLop0,97
|
|
193
|
+
rxiv_maker-1.16.0.dist-info/licenses/LICENSE,sha256=GSZFoPIhWDNJEtSHTQ5dnELN38zFwRiQO2antBezGQk,1093
|
|
194
|
+
rxiv_maker-1.16.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|