hanhou/patchseq
0
1"""Utilities for exporting Bokeh figures to SVG archives."""2 3from __future__ import annotations4 5import io6import zipfile7from datetime import datetime8from typing import Any, Callable, Dict9 10from bokeh.io.export import get_svgs11 12 13def export_figures_to_svg_zip(14 figures, progress_callback: Callable[[int, int], None] | None = None15) -> tuple[io.BytesIO, str]:16 """Return a BytesIO zip archive containing SVG exports of the provided figures.17 18 Supports both Bokeh figures and matplotlib figures wrapped in Panel panes.19 Bokeh figures are exported using Selenium WebDriver, matplotlib figures using savefig().20 21 Uses Bokeh's built-in WebDriver management for Bokeh figures. In Docker, set 22 BOKEH_CHROMEDRIVER_PATH and BOKEH_IN_DOCKER=1 environment variables for proper Chrome detection.23 24 Returns:25 tuple: (zip_buffer, timestamp) where timestamp is formatted as YYYYMMDD_HHMMSS26 """27 available_figures = [(name, fig) for name, fig in figures.items() if fig is not None]28 if not available_figures:29 raise ValueError("No figures available for export.")30 31 zip_buffer = io.BytesIO()32 timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")33 34 total = len(available_figures)35 with zipfile.ZipFile(zip_buffer, "w", compression=zipfile.ZIP_DEFLATED) as archive:36 for idx, (name, fig) in enumerate(available_figures):37 38 # Check if it's a matplotlib figure (wrapped in Panel pane)39 if hasattr(fig, 'object') and hasattr(fig.object, 'savefig'):40 matplotlib_fig = fig.object41 svg_buffer = io.BytesIO()42 matplotlib_fig.savefig(svg_buffer, format='svg', bbox_inches='tight')43 svg_buffer.seek(0)44 svg_content = svg_buffer.getvalue().decode('utf-8')45 archive.writestr(f"{timestamp}_{name}.svg", svg_content)46 else:47 original_backend = fig.output_backend48 fig.output_backend = "svg"49 try:50 svgs = get_svgs(fig)51 finally:52 fig.output_backend = original_backend53 54 if svgs:55 svg_content = "\n".join(svgs)56 archive.writestr(f"{timestamp}_{name}.svg", svg_content)57 58 if progress_callback:59 progress_callback(idx + 1, total)60 61 zip_buffer.seek(0)62 return zip_buffer, timestamp63 