← Files VeraARCHIVED FILE
modules/variance-analysis/scripts/run_variance.py
12.8 KB · Oct 2, 2026 · 00:29 UTC
"""Run deterministic sales variance analysis for Codex interpretation."""
from __future__ import annotations
# Direct CLI calls must select dependencies before importing workflow modules.
if __name__ == "__main__":
import runpy as _runpy
from pathlib import Path as _Path
for _parent in _Path(__file__).resolve().parents:
_launcher = _parent / "scripts" / "self_relaunch.py"
if not _launcher.is_file():
_launcher = _parent / "clara" / "scripts" / "self_relaunch.py"
if _launcher.is_file():
_runpy.run_path(str(_launcher))["ensure_running_in_managed_venv"](__file__)
break
else:
# Standalone components retain their host's dependency setup.
if any(
(_p / "components.json").is_file()
for _p in _Path(__file__).resolve().parents
):
raise SystemExit(
"Managed Python launcher is missing; rebuild the plugin package."
)
import argparse
import logging
import sys
from pathlib import Path
from variance_core import (
add_common_args,
configure_logging,
is_vera_managed_host,
run_variance_analysis,
)
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
for _vendor_root in (
PLUGIN_ROOT / "vendor" / "modules",
PLUGIN_ROOT.parent.parent / "vendor" / "modules",
PLUGIN_ROOT.parent / "_shared" / "vendor" / "modules",
):
if (_vendor_root / "vera_assurance").is_dir():
if str(_vendor_root) not in sys.path:
sys.path.insert(0, str(_vendor_root))
break
from vera_assurance import ( # noqa: E402
AssuranceContractError,
load_client_engagement_context_file,
)
LOGGER = logging.getLogger(__name__)
def parse_move_rows(values: list[str] | None) -> dict[int, list[int]]:
"""Parse legacy move-row CLI mappings such as ``1:1,2``."""
result: dict[int, list[int]] = {}
for value in values or []:
main_text, separator, row_text = value.partition(":")
if not separator:
raise ValueError("Move-row mappings must use MAIN:DRILL[,DRILL...] syntax.")
try:
main_row = int(main_text)
except ValueError as exc:
raise ValueError(f"Invalid main row in move-row mapping: {value}") from exc
if main_row <= 0:
raise ValueError(f"Main row must be positive in move-row mapping: {value}")
drilldown_rows: list[int] = []
for item in row_text.split(","):
try:
drilldown_row = int(item)
except ValueError as exc:
raise ValueError(
f"Invalid drilldown row in move-row mapping: {value}"
) from exc
if drilldown_row <= 0:
raise ValueError(
f"Drilldown row must be positive in move-row mapping: {value}"
)
if drilldown_row not in drilldown_rows:
drilldown_rows.append(drilldown_row)
if drilldown_rows:
result[main_row] = drilldown_rows
return result
def main() -> int:
"""Run deterministic variance calculations."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input_file", type=Path, help="CSV/XLSX sales file.")
parser.add_argument(
"--output-dir",
type=Path,
required=True,
help="Folder where variance outputs will be written.",
)
parser.add_argument("--recipe", type=Path, help="Recipe JSON from inspection.")
parser.add_argument(
"--root-cause-bridge",
action="store_true",
help=(
"Compatibility flag; root-cause variance runs by default when the "
"mapped data supports it."
),
)
parser.add_argument(
"--root-cause-bridge-alternative-result",
type=int,
help="Legacy alternativeResult value for the root-cause bridge.",
)
parser.add_argument(
"--root-cause-bridge-drilldown-row",
action="append",
type=int,
default=[],
help="1-based main bridge row to drill down. Repeat to select multiple rows.",
)
parser.add_argument(
"--root-cause-bridge-drilldown-all",
action="store_true",
help="Run legacy drilldown for every selected root-cause bridge row.",
)
parser.add_argument(
"--root-cause-bridge-move-row",
action="append",
default=[],
help="Move drilldown rows into the main bridge, using MAIN:DRILL[,DRILL...].",
)
parser.add_argument(
"--root-cause-bridge-alternative-sweep",
action="store_true",
help="Run root-cause alternativeResult values as a sweep.",
)
parser.add_argument(
"--root-cause-bridge-alternative-sweep-start",
type=int,
help="First alternativeResult value for the root-cause sweep.",
)
parser.add_argument(
"--root-cause-bridge-alternative-sweep-end",
type=int,
help="Last alternativeResult value for the root-cause sweep.",
)
parser.add_argument(
"--root-cause-bridge-auto-drilldown",
choices=["none", "single-row", "dominant-row", "all-selected"],
help="Automatically drill down selected rows during the sweep.",
)
parser.add_argument(
"--root-cause-bridge-auto-drilldown-min-share",
type=float,
help="Minimum absolute-variance share for dominant-row auto drilldown.",
)
parser.add_argument(
"--root-cause-component-bridge",
action="store_true",
help=(
"Also write the second-order component-by-dimension root-cause "
"bridge. This is separate from the default total-variance bridge."
),
)
parser.add_argument(
"--root-cause-component-bridge-alternative-result",
type=int,
help="Legacy alternativeResult value for the component root-cause bridge.",
)
parser.add_argument(
"--no-waterfall-chart",
action="store_true",
help="Skip the default waterfall.png chart output.",
)
parser.add_argument(
"--waterfall-small-multiples",
action="store_true",
help="Render waterfall.png as small multiples for a reporting dimension.",
)
parser.add_argument(
"--waterfall-small-multiples-dimension",
help="Dimension column to use when rendering waterfall small multiples.",
)
parser.add_argument(
"--total-by-dimension-bridge",
action="store_true",
help=(
"Render the total variance split by one fixed dimension, with row "
"baseline/comparison value bars and percent pins."
),
)
parser.add_argument(
"--no-total-by-dimension-bridge",
action="store_true",
help="Skip the total variance by fixed dimension bridge output.",
)
parser.add_argument(
"--total-by-dimension-bridge-dimension",
help="Dimension column to use for the total variance bridge rows.",
)
parser.add_argument(
"--total-by-dimension-bridge-top-n",
type=int,
help="Maximum member rows before aggregating the remaining members as Other.",
)
parser.add_argument(
"--exploded-variance-bridge",
action="store_true",
help=(
"Render the one-page parent bridge with up to two child drilldown "
"panels."
),
)
parser.add_argument(
"--no-exploded-variance-bridge",
action="store_true",
help="Skip the exploded parent/child variance bridge output.",
)
parser.add_argument(
"--exploded-variance-bridge-parent-dimension",
help="Parent dimension column for the exploded bridge.",
)
parser.add_argument(
"--exploded-variance-bridge-child-dimension",
help="Child drilldown dimension column for the exploded bridge.",
)
parser.add_argument(
"--exploded-variance-bridge-parent-top-n",
type=int,
help="Maximum parent rows before aggregating remaining members as Other.",
)
parser.add_argument(
"--exploded-variance-bridge-child-top-n",
type=int,
help="Maximum child rows per drilldown before aggregating as Other.",
)
parser.add_argument(
"--exploded-variance-bridge-max-drilldowns",
type=int,
help="Maximum expanded parent rows; capped at two.",
)
parser.add_argument(
"--currency",
help="Currency code for monetary outputs. Defaults to EUR when omitted.",
)
parser.add_argument(
"--client-engagement",
type=Path,
required=False,
help=(
"Optional absolute Studio Archive context.json. Vera requires it; "
"standalone and Clara runs may omit it."
),
)
add_common_args(parser)
args = parser.parse_args()
configure_logging(args.verbose)
vera_managed_host = is_vera_managed_host()
if vera_managed_host and args.client_engagement is None:
parser.error("--client-engagement is required by the Vera packaged runner")
client_engagement = None
if args.client_engagement is not None:
if not args.currency:
parser.error("--currency is required for a Vera managed variance run")
input_paths = [args.input_file]
if args.recipe is not None:
input_paths.append(args.recipe)
try:
client_engagement = load_client_engagement_context_file(
args.client_engagement,
expected_workflow_id="variance-analysis",
input_paths=input_paths,
output_dir=args.output_dir,
)
except AssuranceContractError as exc:
parser.error(str(exc))
result = run_variance_analysis(
args.input_file,
args.output_dir,
args.recipe,
root_cause_bridge=(
args.root_cause_bridge or args.root_cause_bridge_alternative_sweep or None
),
root_cause_bridge_alternative_result=(
args.root_cause_bridge_alternative_result
),
root_cause_bridge_drilldown_rows=(args.root_cause_bridge_drilldown_row or None),
root_cause_bridge_drilldown_all=args.root_cause_bridge_drilldown_all or None,
root_cause_bridge_move_rows=parse_move_rows(args.root_cause_bridge_move_row)
or None,
root_cause_bridge_alternative_sweep=(
args.root_cause_bridge_alternative_sweep or None
),
root_cause_bridge_alternative_sweep_start=(
args.root_cause_bridge_alternative_sweep_start
),
root_cause_bridge_alternative_sweep_end=(
args.root_cause_bridge_alternative_sweep_end
),
root_cause_bridge_auto_drilldown=(
args.root_cause_bridge_auto_drilldown.replace("-", "_")
if args.root_cause_bridge_auto_drilldown
else None
),
root_cause_bridge_auto_drilldown_min_share=(
args.root_cause_bridge_auto_drilldown_min_share
),
root_cause_component_bridge=args.root_cause_component_bridge or None,
root_cause_component_bridge_alternative_result=(
args.root_cause_component_bridge_alternative_result
),
waterfall_chart=False if args.no_waterfall_chart else None,
waterfall_small_multiples=args.waterfall_small_multiples or None,
waterfall_small_multiples_dimension=args.waterfall_small_multiples_dimension,
total_by_dimension_bridge=(
False
if args.no_total_by_dimension_bridge
else args.total_by_dimension_bridge or None
),
total_by_dimension_bridge_dimension=(args.total_by_dimension_bridge_dimension),
total_by_dimension_bridge_top_n=args.total_by_dimension_bridge_top_n,
exploded_variance_bridge=(
False
if args.no_exploded_variance_bridge
else args.exploded_variance_bridge or None
),
exploded_variance_bridge_parent_dimension=(
args.exploded_variance_bridge_parent_dimension
),
exploded_variance_bridge_child_dimension=(
args.exploded_variance_bridge_child_dimension
),
exploded_variance_bridge_parent_top_n=(
args.exploded_variance_bridge_parent_top_n
),
exploded_variance_bridge_child_top_n=args.exploded_variance_bridge_child_top_n,
exploded_variance_bridge_max_drilldowns=(
args.exploded_variance_bridge_max_drilldowns
),
currency=args.currency,
language=args.language,
artifact_mode=args.artifact_mode,
client_engagement=client_engagement,
)
LOGGER.info("result_rows=%s", result.frame.height)
LOGGER.info("outputs=%s", sorted(result.audit["outputs"]))
LOGGER.info("wrote %s", args.output_dir / "variance_results.csv")
LOGGER.info("wrote %s", args.output_dir / "variance_audit.json")
return 0
if __name__ == "__main__":
raise SystemExit(main())
SHA-256: 633e4175e2abf9509eacf607eff69ed1dc48f5d34db046402a7ae746bf94109b