Source code for frontend._utils_

# File: _utils_.py
# Code: Claude Code and Codex
# Review: Ryoichi Ando (ryoichi.ando@zozo.com)
# License: Apache v2.0

import contextlib
import os
import platform
import subprocess

from typing import Optional

import psutil  # pyright: ignore[reportMissingModuleSource]

PROCESS_NAME = "ppf-contact"


[docs] def get_cache_dir() -> str: """Get the ppf-cts cache directory. Returns a platform-appropriate cache directory for ppf-cts. - Linux/Mac: ~/.cache/ppf-cts - Windows: <project>/.cache/ppf-cts (project-relative) Returns: str: Path to the ppf-cts cache directory. """ if platform.system() == "Windows": # Use project-relative cache on Windows frontend_dir = os.path.dirname(os.path.realpath(__file__)) base_dir = os.path.dirname(frontend_dir) cache_dir = os.path.join(base_dir, "cache", "ppf-cts") else: # Use ~/.cache/ppf-cts on Linux/Mac cache_dir = os.path.expanduser(os.path.join("~", ".cache", "ppf-cts")) os.makedirs(cache_dir, exist_ok=True) return cache_dir
def get_export_base_path() -> str: """Get the base path for export directories. When fast_check mode is enabled, exports go to the cache directory to avoid leaving artifacts in the examples directory. Otherwise, exports go to the 'export' directory relative to project root. Returns: str: The base path for export directories. """ if Utils.is_fast_check(): # Use cache directory for fast_check mode return os.path.join(get_cache_dir(), "export") else: # Use relative 'export' directory for normal operation return "export" def dict_to_html_table(data: dict, classes: str = "table", index: bool = False) -> str: """Convert a dictionary to an HTML table. Replacement for pandas DataFrame.to_html() to avoid pandas dependency. Args: data: Dictionary where keys are column names and values are lists of values. classes: CSS classes to add to the table element. index: Whether to include row index (ignored, kept for API compatibility). Returns: HTML string representation of the table. """ if not data: return "<table></table>" # Get column names and number of rows columns = list(data.keys()) num_rows = len(next(iter(data.values()))) if data else 0 # Build HTML html_parts = [f'<table class="{classes}">'] # Header html_parts.append("<thead><tr>") for col in columns: html_parts.append(f"<th>{col}</th>") html_parts.append("</tr></thead>") # Body html_parts.append("<tbody>") for i in range(num_rows): html_parts.append("<tr>") for col in columns: val = data[col][i] if i < len(data[col]) else "" html_parts.append(f"<td>{val}</td>") html_parts.append("</tr>") html_parts.append("</tbody>") html_parts.append("</table>") return "".join(html_parts)
[docs] class Utils: """Utility class for frontend."""
[docs] @staticmethod def in_jupyter_notebook(): """Determine if the code is running in a Jupyter notebook.""" dirpath = os.path.dirname(os.path.abspath(__file__)) if os.path.exists(os.path.join(dirpath, ".CLI")) or os.path.exists( os.path.join(dirpath, ".CI") ): return False try: from IPython import get_ipython # type: ignore shell = get_ipython().__class__.__name__ if shell == "ZMQInteractiveShell": return True elif shell == "TerminalInteractiveShell": return False else: return False except (NameError, ImportError): return False
[docs] @staticmethod def ci_name() -> Optional[str]: """Determine if the code is running in a CI environment. Returns: name (str): The name of the CI environment, or an empty string if not in a CI environment. """ dirpath = os.path.dirname(os.path.abspath(__file__)) path = os.path.join(dirpath, ".CI") if os.path.exists(path): with open(path) as f: lines = f.readlines() last_line = "" if len(lines) > 0: last_line = lines[-1].strip() if last_line == "": raise ValueError( "The .CI file is empty. Please add the name of the CI environment." ) else: return last_line else: return None
# Module-level flag for fast check mode (set by App.set_fast_check()) _fast_check_enabled = False
[docs] @staticmethod def is_fast_check() -> bool: """Determine if fast check mode is enabled. Fast check mode forces simulations to run for only 1 frame, enabling quick validation of all examples. Returns: bool: True if fast check mode is enabled. """ return Utils._fast_check_enabled
[docs] @staticmethod def set_fast_check(enabled: bool = True): """Set fast check mode. Args: enabled: Whether to enable fast check mode. """ Utils._fast_check_enabled = enabled
[docs] @staticmethod def get_ci_root() -> str: """Get the path to the CI directory.""" return os.path.join(get_cache_dir(), "ci")
[docs] @staticmethod def get_ci_dir() -> str: """Get the path to the CI local directory.""" ci_name = Utils.ci_name() assert ci_name is not None return os.path.join(Utils.get_ci_root(), ci_name)
[docs] @staticmethod def get_gpu_count(): try: result = subprocess.run( ["nvidia-smi", "-L"], capture_output=True, text=True, check=True ) gpu_count = len(result.stdout.strip().split("\n")) return gpu_count except subprocess.CalledProcessError as e: print("Error occurred while running nvidia-smi:", e) return 0 except FileNotFoundError: print("nvidia-smi not found. Is NVIDIA driver installed?") return 0
[docs] @staticmethod def get_driver_version() -> Optional[int]: try: result = subprocess.run( ["nvidia-smi", "--query-gpu=driver_version", "--format=csv,noheader"], capture_output=True, text=True, check=True, ) driver_version = result.stdout.strip() return int(driver_version.split(".")[0]) except subprocess.CalledProcessError as e: print("Error occurred while running nvidia-smi:", e) return None except FileNotFoundError: print("nvidia-smi not found. Is NVIDIA driver installed?") return None
[docs] @staticmethod def terminate(): """Terminate the solver.""" for proc in psutil.process_iter(["pid", "name", "status"]): if ( PROCESS_NAME in proc.info["name"] and proc.info["status"] != psutil.STATUS_ZOMBIE ): with contextlib.suppress(psutil.NoSuchProcess, psutil.AccessDenied): proc.terminate()
[docs] @staticmethod def busy() -> bool: """Check if the solver is running. Returns: bool: True if the solver is running, False otherwise. """ for proc in psutil.process_iter(["pid", "name", "status"]): if ( PROCESS_NAME in proc.info["name"] and proc.info["status"] != psutil.STATUS_ZOMBIE ): return True return False