fix base costs and import backstage in ctm notebook
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -106,3 +106,10 @@ def test_case_flows_and_kpis():
|
||||
inc2 = {y: -1.0 for y in a4.YEARS}
|
||||
net2 = {y: benefits[y] + 1.0 for y in a4.YEARS}
|
||||
assert a4.case_kpis(inc2, net2)["roi"] is None
|
||||
|
||||
|
||||
def test_contracted_overlays_verbatim():
|
||||
assert a4.tco("ccaas_annual") == 3_200_000 # signed contract
|
||||
assert a4.TCO_VERBATIM["ccaas_annual"] == 4_300_000 # deck record intact
|
||||
assert a4.tco("current_annual") == a4.TCO_VERBATIM["current_annual"]
|
||||
assert a4.licence_costs_by_year(12, a4.tco("ccaas_annual"))[2027] == 3_200_000
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Stage/backstage detection — Mercury kernels carry MERCURY_CONFIG_DIR."""
|
||||
|
||||
from tokencalc import staging
|
||||
|
||||
|
||||
def test_backstage_prints_only_off_stage(monkeypatch, capsys):
|
||||
monkeypatch.delenv("MERCURY_CONFIG_DIR", raising=False)
|
||||
assert not staging.on_stage()
|
||||
staging.backstage("visible")
|
||||
assert capsys.readouterr().out == "visible\n"
|
||||
|
||||
monkeypatch.setenv("MERCURY_CONFIG_DIR", "/tmp/app")
|
||||
assert staging.on_stage()
|
||||
staging.backstage("hidden")
|
||||
assert capsys.readouterr().out == ""
|
||||
@@ -99,6 +99,19 @@ TCO_VERBATIM: dict[str, float] = {
|
||||
"npv_discount_rate": 0.135, # deck's benefit-NPV rate
|
||||
}
|
||||
|
||||
#: Actual contracted values where they differ from the deck — 🟢
|
||||
#: contractual. Layered over TCO_VERBATIM, which stays the untouched
|
||||
#: record of what Genesys pitched (the anchor CTM can follow);
|
||||
#: presentation reads through :func:`tco`.
|
||||
TCO_CONTRACTED: dict[str, float] = {
|
||||
"ccaas_annual": 3_200_000, # signed licence run-rate (deck pitched $4.3M/yr)
|
||||
}
|
||||
|
||||
|
||||
def tco(key: str) -> float:
|
||||
"""Contracted value where one exists, else the deck's verbatim anchor."""
|
||||
return TCO_CONTRACTED.get(key, TCO_VERBATIM[key])
|
||||
|
||||
#: Genesys/Broadreach deployment schedule (slides 17-21), months from
|
||||
#: Jan 2026 inclusive. Benefits realize IMPL + 3 months.
|
||||
IMPL_MONTH = {"NA": 18, "ANZ": 21, "EMEA": 24, "ASIA": 27}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Stage vs backstage — is this notebook render stakeholder-facing?
|
||||
|
||||
The Mercury CLI (``mercury --working-dir …``) exports ``MERCURY_CONFIG_DIR``
|
||||
into the server process so the widget library can locate ``config.toml``
|
||||
(see ``mercury/config.py``); every kernel that server spawns inherits it.
|
||||
JupyterLab and nbconvert kernels don't have it. That makes the variable a
|
||||
reliable signal for "the audience is looking" (the stage) versus an
|
||||
analyst session or a headless export run (backstage).
|
||||
|
||||
Diagnostics routed through :func:`backstage` stay visible in JupyterLab
|
||||
and land in the nbconvert exports (where the machine-readable appendix
|
||||
must appear for LLM consumption) but never render in the Mercury app.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def on_stage() -> bool:
|
||||
"""True when running under the Mercury app (stakeholder-facing)."""
|
||||
return os.getenv("MERCURY_CONFIG_DIR") is not None
|
||||
|
||||
|
||||
def backstage(*args, **kwargs) -> None:
|
||||
"""``print`` that renders only backstage (JupyterLab, nbconvert)."""
|
||||
if not on_stage():
|
||||
print(*args, **kwargs)
|
||||
Reference in New Issue
Block a user