Files
org-notes-quartz/src/org_to_quartz/main.py
Ignacio Ballesteros 79e93c167e quartz build
2026-02-14 18:00:32 +01:00

162 lines
4.7 KiB
Python

"""CLI entry point for org-to-quartz converter."""
import argparse
import sys
from pathlib import Path
from .org_parser import parse_org_file
from .markdown_writer import convert_document, write_markdown
from .image_handler import copy_images, update_image_paths
from .citations import CitationResolver
def find_org_files(input_dir: Path) -> list[Path]:
"""Find all .org files in directory (recursive)."""
return list(input_dir.rglob("*.org"))
def convert_file(
org_path: Path,
input_dir: Path,
output_dir: Path,
citation_resolver: CitationResolver | None,
verbose: bool = False,
) -> Path | None:
"""Convert a single org file to Quartz markdown.
Args:
org_path: Path to the org file
input_dir: Root input directory (for computing relative paths)
output_dir: Root output directory
citation_resolver: Optional citation resolver
verbose: Enable verbose output
Returns:
Path to created note directory, or None on error
"""
try:
# Parse org file
doc = parse_org_file(org_path)
if verbose:
print(f" Parsed: {doc.title or org_path.stem}")
# Compute relative path from input dir to preserve directory structure
relative_path = org_path.relative_to(input_dir)
# Special case: root index.org becomes content/index.md directly
if relative_path.stem == "index" and relative_path.parent == Path("."):
note_dir = output_dir
output_path = output_dir / "index.md"
else:
# Replace .org with directory containing index.md
# e.g., concepts/zettelkasten.org -> concepts/zettelkasten/index.md
note_relative_dir = relative_path.parent / relative_path.stem
note_dir = output_dir / note_relative_dir
output_path = note_dir / "index.md"
# Create output directory for this note
note_dir.mkdir(parents=True, exist_ok=True)
# Copy images first (before conversion, to get path mapping)
image_mapping = copy_images(doc, output_dir)
if verbose and image_mapping:
print(f" Copied {len(image_mapping)} images")
# Convert document
md_content = convert_document(doc, citation_resolver)
# Update image paths in converted content
md_content = update_image_paths(md_content, image_mapping)
# Write output
output_path.write_text(md_content, encoding="utf-8")
return note_dir
except Exception as e:
print(f" Error converting {org_path.name}: {e}", file=sys.stderr)
return None
def main() -> int:
"""Main CLI entry point."""
parser = argparse.ArgumentParser(
prog="org-to-quartz",
description="Convert org-mode notes to Quartz-compatible markdown",
)
parser.add_argument(
"input_dir",
type=Path,
help="Directory containing .org files",
)
parser.add_argument(
"output_dir",
type=Path,
help="Output directory (e.g., quartz/content)",
)
parser.add_argument(
"--bib",
type=Path,
metavar="FILE",
help="Path to .bib file for citation resolution",
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Verbose output",
)
args = parser.parse_args()
# Validate input directory
if not args.input_dir.is_dir():
print(f"Error: Input directory does not exist: {args.input_dir}", file=sys.stderr)
return 1
# Create output directory
args.output_dir.mkdir(parents=True, exist_ok=True)
# Find org files
org_files = find_org_files(args.input_dir)
if not org_files:
print(f"No .org files found in {args.input_dir}", file=sys.stderr)
return 1
print(f"Found {len(org_files)} org files")
# Initialize citation resolver
citation_resolver = CitationResolver(args.bib)
if args.verbose:
if citation_resolver.zotero.is_available():
print("Zotero Better BibTeX: available")
else:
print("Zotero Better BibTeX: not available")
if args.bib:
print(f"BibTeX file: {args.bib}")
# Convert each file
success_count = 0
error_count = 0
for org_path in org_files:
print(f"Converting: {org_path.name}")
result = convert_file(org_path, args.input_dir, args.output_dir, citation_resolver, args.verbose)
if result:
success_count += 1
if args.verbose:
print(f" -> {result}")
else:
error_count += 1
# Summary
print(f"\nDone: {success_count} converted, {error_count} errors")
return 0 if error_count == 0 else 1
if __name__ == "__main__":
sys.exit(main())