"""Plot file processing and HTML rendering.""" import shutil import subprocess from pathlib import Path from typing import Any, Dict from jinja2 import Template from gallery.utils.metadata import ( resolve_metadata_for_plot, get_metadata_file_path, ) from gallery.utils.stats import ( calculate_directory_stats, format_file_size, ) from gallery.config import GalleryConfig def process_html_file( html_file: Path, web_dir: Path, current_metadata: Dict[str, Any] = None ) -> dict: """ Process HTML plot file, copying it to web directory. Args: html_file: Path to the source HTML file web_dir: Target web directory current_metadata: Current metadata dictionary for the plot Returns: Dictionary containing plot information """ web_html = web_dir / html_file.name if needs_update(html_file, web_html): shutil.copy2(html_file, web_html) # Get source file creation time source_creation_time = int(html_file.stat().st_ctime) # Resolve metadata if provided plot_metadata = {} if current_metadata is not None: plot_metadata = resolve_metadata_for_plot(html_file, current_metadata) return { "name": html_file.stem, "html_href": html_file.name, "is_html": True, "metadata": plot_metadata, "creation_time": source_creation_time } def process_plot_files( config: GalleryConfig, plot_file: Path, web_dir: Path, current_metadata: Dict[str, Any] = None, ) -> dict: """ Process plot files (PDF/PNG or HTML), handling conversion and copying. Args: config: Gallery configuration object plot_file: Path to the source plot file (PDF or HTML) web_dir: Target web directory current_metadata: Current metadata dictionary for the plot Returns: Dictionary containing plot information """ if plot_file.suffix.lower() == '.html': return process_html_file(plot_file, web_dir, current_metadata) # Handle PDF files png_file = plot_file.with_suffix(".png") web_pdf = web_dir / plot_file.name web_png = web_dir / png_file.name if needs_update(plot_file, web_pdf): shutil.copy2(plot_file, web_pdf) if not png_file.exists(): convert_pdf_to_png(plot_file, config=config) if needs_update(png_file, web_png): shutil.copy2(png_file, web_png) source_creation_time = int(plot_file.stat().st_ctime) plot_metadata = {} if current_metadata is not None: plot_metadata = resolve_metadata_for_plot(plot_file, current_metadata) return { "name": plot_file.stem, "pdf_href": plot_file.name, "png_href": png_file.name, "is_html": False, "metadata": plot_metadata, "creation_time": source_creation_time } def render_gallery_page( config: GalleryConfig, template: Template, web_dir: Path, items: list, subdirs: list, relative_path: Path, title: str = None, metadata: dict = None ) -> None: """ Unified template rendering for all gallery pages. Args: config: Gallery configuration object template: Jinja2 template object web_dir: Target web directory items: List of plot items subdirs: List of subdirectory names relative_path: Relative path from gallery root title: Page title (optional) metadata: Metadata dictionary (optional) """ if title is None: title = "Gallery" if relative_path == Path( ".") else f"Gallery: {relative_path}" if metadata is None: metadata = {} # Calculate statistics current_stats = calculate_directory_stats(web_dir) stats = { "file_count": len(items), "folder_count": len(subdirs), "total_size": format_file_size(current_stats["total_size"]), "total_size_bytes": current_stats["total_size"] } # Calculate relative path to assets if relative_path == Path("."): assets_path = "../assets" else: depth = len(relative_path.parts) assets_path = "../" * (depth + 1) + "assets" # For root level, show only directory structure if relative_path == Path("."): items = [] output_html = web_dir / "index.html" with output_html.open("w") as f: paths_dict = { "work_dir": str(Path.cwd()), "web_folder": str(config.web_folder), } ui_dict = { "max_recent_plots": 20, "search_debounce_ms": 300, } rendered_html = template.render( title=title, items=items, subdirs=subdirs, relpath=str(relative_path), paths=paths_dict, ui=ui_dict, stats=stats, folder_metadata=metadata, assets_path=assets_path, source_dir=str(web_dir), metadata_file_path=get_metadata_file_path(web_dir) ) f.write(rendered_html) def convert_pdf_to_png(pdf_path: Path, config: GalleryConfig) -> None: """ Convert a PDF file to PNG format using ImageMagick. Only converts if the PNG doesn't exist or if the PDF is newer than the PNG (with a 30-second buffer to handle filesystem timing issues). Args: pdf_path: Path to the source PDF file config: Gallery configuration object Raises: subprocess.CalledProcessError: If ImageMagick conversion fails """ png_path = pdf_path.with_suffix(".png") if png_path.exists(): pdf_mtime = pdf_path.stat().st_mtime png_mtime = png_path.stat().st_mtime if png_mtime >= (pdf_mtime + 30): return subprocess.run([ "convert", "-density", str(config.png_dpi), str(pdf_path), "-quality", "95", str(png_path) ], check=True) def needs_update(source_file: Path, target_file: Path) -> bool: """ Check if target file needs updating based on source modification time. Args: source_file: Path to the source file target_file: Path to the target file Returns: True if target needs update, False otherwise """ if not target_file.exists(): return True source_mtime = source_file.stat().st_mtime target_mtime = target_file.stat().st_mtime return source_mtime > (target_mtime + 30)