Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:30:14 +08:00
commit 1dd5bee3b4
335 changed files with 147360 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
#!/usr/bin/env python3
"""
Customize Template Script
Customize LaTeX templates with author information and project details.
Usage:
python customize_template.py --template nature_article.tex --output my_paper.tex
python customize_template.py --template nature_article.tex --title "My Research" --output my_paper.tex
python customize_template.py --interactive
"""
import argparse
import re
from pathlib import Path
def get_skill_path():
"""Get the path to the venue-templates skill directory."""
script_dir = Path(__file__).parent
skill_dir = script_dir.parent
return skill_dir
def find_template(template_name):
"""Find template file in assets directory."""
skill_path = get_skill_path()
assets_path = skill_path / "assets"
# Search in all subdirectories
for subdir in ["journals", "posters", "grants"]:
template_path = assets_path / subdir / template_name
if template_path.exists():
return template_path
return None
def customize_template(template_path, output_path, **kwargs):
"""Customize a template with provided information."""
# Read template
with open(template_path, 'r') as f:
content = f.read()
# Replace placeholders
replacements = {
'title': (
[r'Insert Your Title Here[^}]*', r'Your [^}]*Title[^}]*Here[^}]*'],
kwargs.get('title', '')
),
'authors': (
[r'First Author\\textsuperscript\{1\}, Second Author[^}]*',
r'First Author.*Second Author.*Third Author'],
kwargs.get('authors', '')
),
'affiliations': (
[r'Department Name, Institution Name, City, State[^\\]*',
r'Department of [^,]*, University Name[^\\]*'],
kwargs.get('affiliations', '')
),
'email': (
[r'first\.author@university\.edu',
r'\[email protected\]'],
kwargs.get('email', '')
)
}
# Apply replacements
modified = False
for key, (patterns, replacement) in replacements.items():
if replacement:
for pattern in patterns:
if re.search(pattern, content):
content = re.sub(pattern, replacement, content, count=1)
modified = True
print(f"✓ Replaced {key}")
# Write output
with open(output_path, 'w') as f:
f.write(content)
if modified:
print(f"\n✓ Customized template saved to: {output_path}")
else:
print(f"\n⚠️ Template copied to: {output_path}")
print(" No customizations applied (no matching placeholders found or no values provided)")
print(f"\nNext steps:")
print(f"1. Open {output_path} in your LaTeX editor")
print(f"2. Replace remaining placeholders")
print(f"3. Add your content")
print(f"4. Compile with pdflatex or your preferred LaTeX compiler")
def interactive_mode():
"""Run in interactive mode."""
print("\n=== Template Customization (Interactive Mode) ===\n")
# List available templates
skill_path = get_skill_path()
assets_path = skill_path / "assets"
print("Available templates:\n")
templates = []
for i, subdir in enumerate(["journals", "posters", "grants"], 1):
subdir_path = assets_path / subdir
if subdir_path.exists():
print(f"{subdir.upper()}:")
for j, template_file in enumerate(sorted(subdir_path.glob("*.tex")), 1):
templates.append(template_file)
print(f" {len(templates)}. {template_file.name}")
print()
# Select template
while True:
try:
choice = int(input(f"Select template (1-{len(templates)}): "))
if 1 <= choice <= len(templates):
template_path = templates[choice - 1]
break
else:
print(f"Please enter a number between 1 and {len(templates)}")
except ValueError:
print("Please enter a valid number")
print(f"\nSelected: {template_path.name}\n")
# Get customization info
title = input("Paper title (press Enter to skip): ").strip()
authors = input("Authors (e.g., 'John Doe, Jane Smith') (press Enter to skip): ").strip()
affiliations = input("Affiliations (press Enter to skip): ").strip()
email = input("Corresponding email (press Enter to skip): ").strip()
# Output file
default_output = f"my_{template_path.stem}.tex"
output = input(f"Output filename [{default_output}]: ").strip()
if not output:
output = default_output
output_path = Path(output)
# Customize
print()
customize_template(
template_path,
output_path,
title=title,
authors=authors,
affiliations=affiliations,
email=email
)
def main():
parser = argparse.ArgumentParser(
description="Customize LaTeX templates with author and project information",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --interactive
%(prog)s --template nature_article.tex --output my_paper.tex
%(prog)s --template neurips_article.tex --title "My ML Research" --output my_neurips.tex
"""
)
parser.add_argument('--template', type=str, help='Template filename')
parser.add_argument('--output', type=str, help='Output filename')
parser.add_argument('--title', type=str, help='Paper title')
parser.add_argument('--authors', type=str, help='Author names')
parser.add_argument('--affiliations', type=str, help='Institutions/affiliations')
parser.add_argument('--email', type=str, help='Corresponding author email')
parser.add_argument('--interactive', action='store_true', help='Run in interactive mode')
args = parser.parse_args()
# Interactive mode
if args.interactive:
interactive_mode()
return
# Command-line mode
if not args.template or not args.output:
print("Error: --template and --output are required (or use --interactive)")
parser.print_help()
return
# Find template
template_path = find_template(args.template)
if not template_path:
print(f"Error: Template '{args.template}' not found")
print("\nSearched in:")
skill_path = get_skill_path()
for subdir in ["journals", "posters", "grants"]:
print(f" - {skill_path}/assets/{subdir}/")
return
# Customize
output_path = Path(args.output)
customize_template(
template_path,
output_path,
title=args.title,
authors=args.authors,
affiliations=args.affiliations,
email=args.email
)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,260 @@
#!/usr/bin/env python3
"""
Query Template Script
Search and retrieve venue-specific templates by name, type, or keywords.
Usage:
python query_template.py --venue "Nature" --type "article"
python query_template.py --keyword "machine learning"
python query_template.py --list-all
python query_template.py --venue "NeurIPS" --requirements
"""
import argparse
import os
import json
from pathlib import Path
# Template database
TEMPLATES = {
"journals": {
"nature": {
"file": "nature_article.tex",
"full_name": "Nature",
"description": "Top-tier multidisciplinary science journal",
"page_limit": "~3000 words",
"citation_style": "Superscript numbered",
"format": "Single column"
},
"neurips": {
"file": "neurips_article.tex",
"full_name": "NeurIPS (Neural Information Processing Systems)",
"description": "Top-tier machine learning conference",
"page_limit": "8 pages + unlimited refs",
"citation_style": "Numbered [1]",
"format": "Two column",
"anonymization": "Required (double-blind)"
},
"plos_one": {
"file": "plos_one.tex",
"full_name": "PLOS ONE",
"description": "Open-access multidisciplinary journal",
"page_limit": "No limit",
"citation_style": "Vancouver [1]",
"format": "Single column"
}
},
"posters": {
"beamerposter": {
"file": "beamerposter_academic.tex",
"full_name": "Beamerposter Academic",
"description": "Classic academic conference poster using beamerposter",
"size": "A0, customizable",
"package": "beamerposter"
}
},
"grants": {
"nsf": {
"file": "nsf_proposal_template.tex",
"full_name": "NSF Standard Grant",
"description": "National Science Foundation research proposal",
"page_limit": "15 pages (project description)",
"key_sections": "Project Summary, Project Description, Broader Impacts"
},
"nih_specific_aims": {
"file": "nih_specific_aims.tex",
"full_name": "NIH Specific Aims Page",
"description": "Most critical page of NIH proposals",
"page_limit": "1 page (strictly enforced)",
"key_sections": "Hook, Hypothesis, 3 Aims, Payoff"
}
}
}
def get_skill_path():
"""Get the path to the venue-templates skill directory."""
# Assume script is in .claude/skills/venue-templates/scripts/
script_dir = Path(__file__).parent
skill_dir = script_dir.parent
return skill_dir
def search_templates(venue=None, template_type=None, keyword=None):
"""Search for templates matching criteria."""
results = []
for cat_name, category in TEMPLATES.items():
# Filter by type if specified
if template_type and cat_name != template_type and template_type != "all":
continue
for temp_id, template in category.items():
# Filter by venue name
if venue:
venue_lower = venue.lower()
if venue_lower not in temp_id and venue_lower not in template.get("full_name", "").lower():
continue
# Filter by keyword
if keyword:
keyword_lower = keyword.lower()
search_text = json.dumps(template).lower()
if keyword_lower not in search_text:
continue
results.append({
"id": temp_id,
"category": cat_name,
"file": template["file"],
"full_name": template.get("full_name", temp_id),
"description": template.get("description", ""),
"details": template
})
return results
def list_all_templates():
"""List all available templates."""
print("\n=== AVAILABLE TEMPLATES ===\n")
for cat_name, category in TEMPLATES.items():
print(f"\n{cat_name.upper()}:")
for temp_id, template in category.items():
print(f"{template.get('full_name', temp_id)}")
print(f" File: {template['file']}")
if "description" in template:
print(f" Description: {template['description']}")
print()
def print_template_info(template):
"""Print detailed information about a template."""
print(f"\n{'='*60}")
print(f"Template: {template['full_name']}")
print(f"{'='*60}")
print(f"Category: {template['category']}")
print(f"File: {template['file']}")
details = template['details']
print(f"\nDescription: {details.get('description', 'N/A')}")
if 'page_limit' in details:
print(f"Page Limit: {details['page_limit']}")
if 'citation_style' in details:
print(f"Citation Style: {details['citation_style']}")
if 'format' in details:
print(f"Format: {details['format']}")
if 'anonymization' in details:
print(f"⚠️ Anonymization: {details['anonymization']}")
if 'size' in details:
print(f"Poster Size: {details['size']}")
if 'package' in details:
print(f"LaTeX Package: {details['package']}")
if 'key_sections' in details:
print(f"Key Sections: {details['key_sections']}")
# Print full path to template
skill_path = get_skill_path()
template_path = skill_path / "assets" / template['category'] / template['file']
print(f"\nFull Path: {template_path}")
if template_path.exists():
print("✓ Template file exists")
else:
print("✗ Template file not found")
print()
def print_requirements(venue):
"""Print formatting requirements for a venue."""
results = search_templates(venue=venue)
if not results:
print(f"No templates found for venue: {venue}")
return
template = results[0] # Take first match
details = template['details']
print(f"\n{'='*60}")
print(f"FORMATTING REQUIREMENTS: {template['full_name']}")
print(f"{'='*60}\n")
if 'page_limit' in details:
print(f"📄 Page Limit: {details['page_limit']}")
if 'format' in details:
print(f"📐 Format: {details['format']}")
if 'citation_style' in details:
print(f"📚 Citation Style: {details['citation_style']}")
if 'anonymization' in details:
print(f"🔒 Anonymization: {details['anonymization']}")
if 'size' in details:
print(f"📏 Size: {details['size']}")
print(f"\n💡 For detailed requirements, see:")
skill_path = get_skill_path()
if template['category'] == "journals":
print(f" {skill_path}/references/journals_formatting.md")
elif template['category'] == "posters":
print(f" {skill_path}/references/posters_guidelines.md")
elif template['category'] == "grants":
print(f" {skill_path}/references/grants_requirements.md")
print()
def main():
parser = argparse.ArgumentParser(
description="Query venue-specific LaTeX templates",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --list-all
%(prog)s --venue "Nature" --type journals
%(prog)s --keyword "machine learning"
%(prog)s --venue "NeurIPS" --requirements
"""
)
parser.add_argument('--venue', type=str, help='Venue name (e.g., "Nature", "NeurIPS")')
parser.add_argument('--type', type=str, choices=['journals', 'posters', 'grants', 'all'],
help='Template type')
parser.add_argument('--keyword', type=str, help='Search keyword')
parser.add_argument('--list-all', action='store_true', help='List all available templates')
parser.add_argument('--requirements', action='store_true',
help='Show formatting requirements for venue')
args = parser.parse_args()
# List all templates
if args.list_all:
list_all_templates()
return
# Show requirements
if args.requirements:
if not args.venue:
print("Error: --requirements requires --venue")
parser.print_help()
return
print_requirements(args.venue)
return
# Search for templates
if not any([args.venue, args.type, args.keyword]):
parser.print_help()
return
results = search_templates(venue=args.venue, template_type=args.type, keyword=args.keyword)
if not results:
print("No templates found matching your criteria.")
return
print(f"\nFound {len(results)} template(s):\n")
for result in results:
print_template_info(result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,255 @@
#!/usr/bin/env python3
"""
Validate Format Script
Check if document meets venue-specific formatting requirements.
Usage:
python validate_format.py --file my_paper.pdf --venue "Nature" --check-all
python validate_format.py --file my_paper.pdf --venue "NeurIPS" --check page-count,margins
python validate_format.py --file my_paper.pdf --venue "PLOS ONE" --report validation_report.txt
"""
import argparse
import subprocess
from pathlib import Path
import re
# Venue requirements database
VENUE_REQUIREMENTS = {
"nature": {
"page_limit": 5, # Approximate for ~3000 words
"margins": {"top": 2.5, "bottom": 2.5, "left": 2.5, "right": 2.5}, # cm
"font_size": 12, # pt
"font_family": "Times",
"line_spacing": "double"
},
"neurips": {
"page_limit": 8, # Excluding refs
"margins": {"top": 2.54, "bottom": 2.54, "left": 2.54, "right": 2.54}, # cm (1 inch)
"font_size": 10,
"font_family": "Times",
"format": "two-column"
},
"plos_one": {
"page_limit": None, # No limit
"margins": {"top": 2.54, "bottom": 2.54, "left": 2.54, "right": 2.54},
"font_size": 10,
"font_family": "Arial",
"line_spacing": "double"
},
"nsf": {
"page_limit": 15, # Project description
"margins": {"top": 2.54, "bottom": 2.54, "left": 2.54, "right": 2.54}, # 1 inch required
"font_size": 11, # Minimum
"font_family": "Times Roman",
"line_spacing": "single or double"
},
"nih": {
"page_limit": 12, # Research strategy
"margins": {"top": 1.27, "bottom": 1.27, "left": 1.27, "right": 1.27}, # 0.5 inch minimum
"font_size": 11, # Arial 11pt minimum
"font_family": "Arial",
"line_spacing": "any"
}
}
def get_pdf_info(pdf_path):
"""Extract information from PDF using pdfinfo."""
try:
result = subprocess.run(
['pdfinfo', str(pdf_path)],
capture_output=True,
text=True,
check=True
)
info = {}
for line in result.stdout.split('\n'):
if ':' in line:
key, value = line.split(':', 1)
info[key.strip()] = value.strip()
return info
except FileNotFoundError:
print("⚠️ pdfinfo not found. Install poppler-utils for full PDF analysis.")
print(" macOS: brew install poppler")
print(" Linux: sudo apt-get install poppler-utils")
return None
except subprocess.CalledProcessError as e:
print(f"Error running pdfinfo: {e}")
return None
def check_page_count(pdf_path, venue_reqs):
"""Check if page count is within limit."""
pdf_info = get_pdf_info(pdf_path)
if not pdf_info:
return {"status": "skip", "message": "Could not determine page count"}
pages = int(pdf_info.get('Pages', 0))
limit = venue_reqs.get('page_limit')
if limit is None:
return {"status": "pass", "message": f"No page limit. Document has {pages} pages."}
if pages <= limit:
return {"status": "pass", "message": f"✓ Page count OK: {pages}/{limit} pages"}
else:
return {"status": "fail", "message": f"✗ Page count exceeded: {pages}/{limit} pages"}
def check_margins(pdf_path, venue_reqs):
"""Check if margins meet requirements."""
# Note: This is a simplified check. Full margin analysis requires more sophisticated tools.
req_margins = venue_reqs.get('margins', {})
if not req_margins:
return {"status": "skip", "message": "No margin requirements specified"}
# This is a placeholder - accurate margin checking requires parsing PDF content
return {
"status": "info",
"message": f" Required margins: {req_margins} cm (manual verification recommended)"
}
def check_fonts(pdf_path, venue_reqs):
"""Check fonts in PDF."""
try:
result = subprocess.run(
['pdffonts', str(pdf_path)],
capture_output=True,
text=True,
check=True
)
fonts_found = []
for line in result.stdout.split('\n')[2:]: # Skip header
if line.strip():
parts = line.split()
if parts:
fonts_found.append(parts[0])
req_font = venue_reqs.get('font_family', '')
req_size = venue_reqs.get('font_size')
message = f" Fonts found: {', '.join(set(fonts_found))}\n"
message += f" Required: {req_font}"
if req_size:
message += f" {req_size}pt minimum"
return {"status": "info", "message": message}
except FileNotFoundError:
return {"status": "skip", "message": "pdffonts not available"}
except subprocess.CalledProcessError:
return {"status": "skip", "message": "Could not extract font information"}
def validate_document(pdf_path, venue, checks):
"""Validate document against venue requirements."""
venue_key = venue.lower().replace(" ", "_")
if venue_key not in VENUE_REQUIREMENTS:
print(f"❌ Unknown venue: {venue}")
print(f"Available venues: {', '.join(VENUE_REQUIREMENTS.keys())}")
return
venue_reqs = VENUE_REQUIREMENTS[venue_key]
print(f"\n{'='*60}")
print(f"VALIDATING: {pdf_path.name}")
print(f"VENUE: {venue}")
print(f"{'='*60}\n")
results = {}
# Run requested checks
if 'page-count' in checks or 'all' in checks:
results['page-count'] = check_page_count(pdf_path, venue_reqs)
if 'margins' in checks or 'all' in checks:
results['margins'] = check_margins(pdf_path, venue_reqs)
if 'fonts' in checks or 'all' in checks:
results['fonts'] = check_fonts(pdf_path, venue_reqs)
# Print results
for check_name, result in results.items():
print(f"{check_name.upper()}:")
print(f" {result['message']}\n")
# Summary
failures = sum(1 for r in results.values() if r['status'] == 'fail')
passes = sum(1 for r in results.values() if r['status'] == 'pass')
print(f"{'='*60}")
if failures == 0:
print(f"✓ VALIDATION PASSED ({passes} checks)")
else:
print(f"✗ VALIDATION FAILED ({failures} issues)")
print(f"{'='*60}\n")
return results
def generate_report(pdf_path, venue, results, report_path):
"""Generate validation report."""
with open(report_path, 'w') as f:
f.write(f"Validation Report\n")
f.write(f"{'='*60}\n\n")
f.write(f"File: {pdf_path}\n")
f.write(f"Venue: {venue}\n")
f.write(f"Date: {Path.ctime(pdf_path)}\n\n")
for check_name, result in results.items():
f.write(f"{check_name.upper()}:\n")
f.write(f" Status: {result['status']}\n")
f.write(f" {result['message']}\n\n")
failures = sum(1 for r in results.values() if r['status'] == 'fail')
f.write(f"\nSummary: {'PASSED' if failures == 0 else 'FAILED'}\n")
print(f"Report saved to: {report_path}")
def main():
parser = argparse.ArgumentParser(
description="Validate document formatting for venue requirements",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --file my_paper.pdf --venue "Nature" --check-all
%(prog)s --file my_paper.pdf --venue "NeurIPS" --check page-count,fonts
%(prog)s --file proposal.pdf --venue "NSF" --report validation.txt
"""
)
parser.add_argument('--file', type=str, required=True, help='PDF file to validate')
parser.add_argument('--venue', type=str, required=True, help='Target venue')
parser.add_argument('--check', type=str, default='all',
help='Checks to perform: page-count, margins, fonts, all (comma-separated)')
parser.add_argument('--check-all', action='store_true', help='Perform all checks')
parser.add_argument('--report', type=str, help='Save report to file')
args = parser.parse_args()
# Check file exists
pdf_path = Path(args.file)
if not pdf_path.exists():
print(f"Error: File not found: {pdf_path}")
return
# Parse checks
if args.check_all:
checks = ['all']
else:
checks = [c.strip() for c in args.check.split(',')]
# Validate
results = validate_document(pdf_path, args.venue, checks)
# Generate report if requested
if args.report and results:
generate_report(pdf_path, args.venue, results, Path(args.report))
if __name__ == "__main__":
main()