Source code for swcstudio.tools.validation.features.auto_typing

"""Single-file auto-typing for the Validation tool.

Thin wrapper around :mod:`swcstudio.core.auto_typing`. The engine itself
is the v12 QC-label-flag pipeline; there is no backend selection. The
runtime knobs are the user model directory override, optional cell-type
override, flag scoring strictness, and whether to use the subtree Stage
2 head.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any

import pandas as pd

from swcstudio.core.auto_typing import BatchOptions, run_file as _run_engine_file
from swcstudio.core.auto_typing_results import (
    auto_typing_result_to_dataframe,
    merge_labeled_types_only,
)
from swcstudio.core.config import merge_config
from swcstudio.core.reporting import (
    operation_output_path_for_file,
    resolve_requested_output_path_for_file,
    timestamp_slug,
)
from swcstudio.core.swc_io import (
    parse_swc_text_preserve_tokens,
    write_swc_to_bytes_preserve_tokens,
)
from swcstudio.tools.batch_processing.features.auto_typing import (
    get_config as get_batch_auto_config,
)


def _options_from_config(cfg: dict[str, Any]) -> BatchOptions:
    return BatchOptions(
        soma=True,
        axon=True,
        apic=True,
        basal=True,
        rad=False,
        zip_output=False,
        cell_type=(cfg.get("cell_type") or "unknown"),
        flag_enabled=bool(cfg.get("flag_enabled", True)),
        flag_strictness=float(cfg.get("flag_strictness", 0.5)),
        flag_feature_mode="compact",
    )


[docs] def run_file( file_path: str, *, options: BatchOptions | None = None, config_overrides: dict | None = None, output_path: str | None = None, write_output: bool = True, write_log: bool = True, ): """Run auto-typing on a single file using the configured engine.""" cfg = get_batch_auto_config() if isinstance(config_overrides, dict) and config_overrides: cfg = merge_config(cfg, config_overrides) opts = options if options is not None else _options_from_config(cfg) return _run_engine_file( file_path, opts, output_path=output_path, write_output=write_output, write_log=write_log, model_dir=(cfg.get("model_dir") or None), use_subtree_stage2=bool(cfg.get("use_subtree_stage2", True)), )
def result_to_dataframe(result: object) -> pd.DataFrame: return auto_typing_result_to_dataframe(result) def merge_types_only(base_df: pd.DataFrame, labeled_df: pd.DataFrame) -> pd.DataFrame: return merge_labeled_types_only(base_df, labeled_df) def auto_label_file( file_path: str, *, options: BatchOptions | None = None, config_overrides: dict | None = None, output_path: str | None = None, write_output: bool = True, write_log: bool = False, ) -> dict[str, Any]: in_path = Path(file_path) if not in_path.exists(): raise FileNotFoundError(file_path) base_df = parse_swc_text_preserve_tokens(in_path.read_text(encoding="utf-8", errors="ignore")) result_obj = run_file( file_path, options=options, config_overrides=config_overrides, output_path=None, write_output=False, write_log=write_log, ) labeled_df = result_to_dataframe(result_obj) merged_df = merge_types_only(base_df, labeled_df) out_bytes = write_swc_to_bytes_preserve_tokens(merged_df) out_path: Path | None = None run_timestamp = timestamp_slug() if write_output: out_path = ( resolve_requested_output_path_for_file(in_path, output_path) if output_path else operation_output_path_for_file(in_path, "validation_auto_label", timestamp=run_timestamp) ) out_path.write_bytes(out_bytes) return { "dataframe": merged_df, "bytes": out_bytes, "input_path": str(in_path), "output_path": str(out_path) if out_path is not None else None, "nodes_total": int(getattr(result_obj, "nodes_total", 0)), "type_changes": int(getattr(result_obj, "type_changes", 0)), "radius_changes": 0, "out_type_counts": dict(getattr(result_obj, "out_type_counts", {}) or {}), "cell_type": getattr(result_obj, "cell_type", None), "cell_type_source": getattr(result_obj, "cell_type_source", "stage1"), "stage1_confidence": getattr(result_obj, "stage1_confidence", None), "qc_result": dict(getattr(result_obj, "qc_result", {}) or {}), "flag_result": dict(getattr(result_obj, "flag_result", {}) or {}), "change_details": list(getattr(result_obj, "change_details", []) or []), "result_obj": result_obj, } __all__ = [ "BatchOptions", "run_file", "result_to_dataframe", "merge_types_only", "auto_label_file", ]