Token Calculator
This commit is contained in:
@@ -32,7 +32,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"✅ Athena connected — https://athena.ouranos.helu.ca (1 report templates visible)\n",
|
||||
"✅ Athena connected — https://athena.ouranos.helu.ca (2 report templates visible)\n",
|
||||
"📁 Study: 202512_GenesysCX\n"
|
||||
]
|
||||
}
|
||||
@@ -69,7 +69,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Created report template UCb2hSJprSBx\n"
|
||||
"Found existing report template UCb2hSJprSBx (status: active)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -114,7 +114,7 @@
|
||||
"`(1 + risk_adj)`; Year-0 amounts use companion `*_initial` fields.\n",
|
||||
"The `genesys_ai_tokens` line is seeded \\$0 (reproduces the published study) —\n",
|
||||
"the annual cost gets entered per deal, from the Genesys quote, in\n",
|
||||
"`01_business_case.ipynb`."
|
||||
"`03_business_case.ipynb`."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -127,8 +127,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"12 fields created, 0 already existed.\n",
|
||||
"Report template activated.\n"
|
||||
"0 fields created, 12 already existed.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -305,7 +304,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 5,
|
||||
"id": "1e375b54",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -445,7 +444,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 6,
|
||||
"id": "584e01dd",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -531,7 +530,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": 7,
|
||||
"id": "e04b1676",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -539,7 +538,6 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Auto-selected proposal 1: Secure Cloud Infrastructure Modernization\n",
|
||||
"Attaching via: {'proposal': 1}\n"
|
||||
]
|
||||
}
|
||||
@@ -589,7 +587,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"execution_count": 8,
|
||||
"id": "0655d1fc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -597,7 +595,7 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Created tool 3rzDgVdsjhVv attached to {'proposal': 1}\n"
|
||||
"Found existing tool 3rzDgVdsjhVv (status: draft)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -643,7 +641,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"execution_count": 9,
|
||||
"id": "86443d76",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -696,7 +694,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": 10,
|
||||
"id": "0728b42e",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -724,7 +722,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"execution_count": 11,
|
||||
"id": "aba8fc21",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -853,7 +851,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"execution_count": 12,
|
||||
"id": "d8102590",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@@ -861,13 +859,12 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Saved version 1 (baseline).\n",
|
||||
"Saved to /Users/robert/git/palladium/.env:\n",
|
||||
" PALLADIUM_GENESYSCX_REPORT_PUBLIC_ID=UCb2hSJprSBx\n",
|
||||
" PALLADIUM_GENESYSCX_TOOL_PUBLIC_ID=3rzDgVdsjhVv\n",
|
||||
" PALLADIUM_GENESYSCX_PROPOSAL_ID=1\n",
|
||||
"\n",
|
||||
"Next → 01_business_case.ipynb (AI token cost entry + sensitivity).\n"
|
||||
"Next → 01_benefits.ipynb (walk through the four Forrester benefits).\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -876,7 +873,7 @@
|
||||
" client.save_version(TOOL_ID, note=(\n",
|
||||
" \"Baseline — published Forrester CX Cloud TEI figures (Dec 2025). \"\n",
|
||||
" \"genesys_ai_tokens at $0 per the published study; set the annual \"\n",
|
||||
" \"cost from the Genesys quote in 01_business_case before client use.\"\n",
|
||||
" \"cost from the Genesys quote in 03_business_case before client use.\"\n",
|
||||
" ))\n",
|
||||
" print(\"Saved version 1 (baseline).\")\n",
|
||||
"\n",
|
||||
@@ -893,7 +890,7 @@
|
||||
"print(f\"Saved to {env_path}:\")\n",
|
||||
"for k, v in ids.items():\n",
|
||||
" print(f\" {k}={v}\")\n",
|
||||
"print(\"\\nNext → 01_business_case.ipynb (AI token cost entry + sensitivity).\")"
|
||||
"print(\"\\nNext → 01_benefits.ipynb (walk through the four Forrester benefits).\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -903,6 +900,14 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "13acdc34-71f6-4220-8675-4e1527cb8e39",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
1408
studies/202512_GenesysCX/notebooks/01_benefits.ipynb
Normal file
1408
studies/202512_GenesysCX/notebooks/01_benefits.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -1,376 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3da9de99",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 01 · Business Case — Genesys CX Cloud TEI\n",
|
||||
"\n",
|
||||
"Working view of the live Athena tool, plus the **Genesys AI Experience token**\n",
|
||||
"cost line the published study omits. Run `00_provision.ipynb` first.\n",
|
||||
"\n",
|
||||
"The published study models \\$0 AI consumption while three of its four benefits\n",
|
||||
"(self-service uplift, agent efficiency, agent-assist sales) depend on AI\n",
|
||||
"capabilities that Genesys bills via AI Experience tokens. Token pricing is\n",
|
||||
"tiered and deal-specific, so the model keeps it simple — **one annual cost\n",
|
||||
"value, entered from the Genesys quote**, exactly as Athena stores it. Quote\n",
|
||||
"details (volume, unit price, tier) go in the field notes for the audit trail."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "89deae70",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib # path shim: works on a fresh kernel\n",
|
||||
"for _p in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (_p / \"pyproject.toml\").exists():\n",
|
||||
" sys.path.insert(0, str(_p)); break\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from core.bootstrap import init\n",
|
||||
"from core.notebook_helpers import charts\n",
|
||||
"\n",
|
||||
"pal = init(study=\"202512_GenesysCX\")\n",
|
||||
"client, seed, config = pal.client, pal.seed_data, pal.config\n",
|
||||
"\n",
|
||||
"TOOL_ID = config.TOOL_PUBLIC_ID\n",
|
||||
"assert TOOL_ID, \"No PALLADIUM_GENESYSCX_TOOL_PUBLIC_ID in .env — run 00_provision.ipynb first.\"\n",
|
||||
"tool = client.get_tool(TOOL_ID)\n",
|
||||
"print(f\"Tool: {tool.get('name')} ({TOOL_ID}) status={tool.get('status')}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "09afaf70",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Current financial summary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3d80c19a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"summary = client.calculate(TOOL_ID)\n",
|
||||
"client.print_summary(TOOL_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "392df0ba",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"values = client.get_values(TOOL_ID)\n",
|
||||
"benefit_rows = [v for v in values if v.get(\"table\") == \"benefits\"]\n",
|
||||
"cost_rows = [v for v in values if v.get(\"table\") == \"costs\"]\n",
|
||||
"\n",
|
||||
"def value_table(rows, *, initial=False):\n",
|
||||
" out = []\n",
|
||||
" for v in rows:\n",
|
||||
" yv = v.get(\"year_values\") or {}\n",
|
||||
" rec = {\"field\": v.get(\"label\") or v[\"field_key\"]}\n",
|
||||
" if initial:\n",
|
||||
" rec[\"Initial\"] = v.get(\"initial\", 0.0)\n",
|
||||
" rec.update({f\"Year {y}\": yv.get(str(y), 0.0) for y in (1, 2, 3)})\n",
|
||||
" rec[\"risk_adj\"] = v.get(\"risk_adjustment\")\n",
|
||||
" out.append(rec)\n",
|
||||
" return pd.DataFrame(out)\n",
|
||||
"\n",
|
||||
"print(\"Benefits (nominal; Athena risk-adjusts at calculate time):\")\n",
|
||||
"display(value_table(benefit_rows))\n",
|
||||
"print(\"Costs (stored pre-multiplied by 1 + risk_adj):\")\n",
|
||||
"display(value_table(cost_rows, initial=True))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2383f761",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Financial visualizations\n",
|
||||
"\n",
|
||||
"All figures are risk-adjusted and built from the live Athena values, using\n",
|
||||
"the shared `core.notebook_helpers.charts` helpers (same ones the Streamlit\n",
|
||||
"app uses)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ── Visual theme — plain values, edit freely (not confidential) ──\n",
|
||||
"THEME = {\n",
|
||||
" \"heading_font\": \"Helvetica Neue, Arial, sans-serif\", # chart titles\n",
|
||||
" \"body_font\": \"Helvetica, Arial, sans-serif\", # axes, legends, labels\n",
|
||||
" \"font_color\": \"#1F2937\", # hex\n",
|
||||
"\n",
|
||||
" # Circle-chart slice colours, applied in order a..j\n",
|
||||
" \"pie_colors\": {\n",
|
||||
" \"a\": \"#1565C0\",\n",
|
||||
" \"b\": \"#2E7D32\",\n",
|
||||
" \"c\": \"#C62828\",\n",
|
||||
" \"d\": \"#F9A825\",\n",
|
||||
" \"e\": \"#6A1B9A\",\n",
|
||||
" \"f\": \"#00838F\",\n",
|
||||
" \"g\": \"#EF6C00\",\n",
|
||||
" \"h\": \"#5D4037\",\n",
|
||||
" \"i\": \"#37474F\",\n",
|
||||
" \"j\": \"#AD1457\",\n",
|
||||
" },\n",
|
||||
"\n",
|
||||
" \"bar_green\": \"#2E7D32\", # benefits bars\n",
|
||||
" \"bar_red\": \"#C62828\", # costs bars\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"charts.apply_theme(**THEME)\n",
|
||||
"print(\"Theme applied — re-run the chart cells below to restyle.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "14d5942a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cost breakdown — share of total three-year cost per line item\n",
|
||||
"charts.cost_breakdown_pie(\n",
|
||||
" cost_rows, title=\"Cost Breakdown — share of 3-year total (risk-adjusted)\"\n",
|
||||
").show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d13f342e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Benefits — three-year risk-adjusted total per category\n",
|
||||
"charts.benefits_bar(\n",
|
||||
" benefit_rows, title=\"Benefits (Three-Year, Risk-Adjusted)\"\n",
|
||||
").show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9ef6caee",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Benefits vs costs, year by year (Initial = one-time Year-0 costs)\n",
|
||||
"charts.benefits_vs_costs_by_year(benefit_rows, cost_rows).show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6f069df6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cash flow with cumulative net benefits — the Forrester-style exhibit\n",
|
||||
"def _yearly_breakdown(benefit_items, cost_items):\n",
|
||||
" \"\"\"Risk-adjusted yearly rows + initial, computed from the live values.\"\"\"\n",
|
||||
" initial = sum(float(c.get(\"initial\") or 0) for c in cost_items)\n",
|
||||
" rows, cumulative = [], -initial\n",
|
||||
" for y in (1, 2, 3):\n",
|
||||
" b = sum(float((v.get(\"year_values\") or {}).get(str(y), 0) or 0)\n",
|
||||
" * (1 - float(v.get(\"risk_adjustment\") or 0))\n",
|
||||
" for v in benefit_items)\n",
|
||||
" c = sum(float((v.get(\"year_values\") or {}).get(str(y), 0) or 0)\n",
|
||||
" for v in cost_items)\n",
|
||||
" cumulative += b - c\n",
|
||||
" rows.append({\"year\": y, \"benefits\": b, \"costs\": c,\n",
|
||||
" \"net\": b - c, \"cumulative_net\": cumulative})\n",
|
||||
" return rows, initial\n",
|
||||
"\n",
|
||||
"yb, initial_cost = _yearly_breakdown(benefit_rows, cost_rows)\n",
|
||||
"charts.cashflow_chart(yb, initial_cost=initial_cost).show()\n",
|
||||
"pd.DataFrame(yb)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b1ff0315",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Waterfall — Benefits PV down to NPV (from the Athena summary)\n",
|
||||
"charts.waterfall([\n",
|
||||
" (\"Benefits PV\", float(summary[\"total_benefits_pv\"])),\n",
|
||||
" (\"Costs PV\", -float(summary[\"total_costs_pv\"])),\n",
|
||||
" (\"NPV\", float(summary[\"net_present_value\"])),\n",
|
||||
"], title=\"Benefits PV → Costs PV → NPV\").show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c78f77a9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Genesys AI Experience tokens — annual cost\n",
|
||||
"\n",
|
||||
"Enter the negotiated annual token cost from the Genesys quote. For sizing\n",
|
||||
"context, the study's own drivers imply roughly **1,040,000** self-service\n",
|
||||
"interactions/yr and **3,120,000** agent-assisted interactions/yr would draw\n",
|
||||
"tokens — bring the actual figure from the quote, not a derivation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "99b9c665",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ── Deal inputs ──\n",
|
||||
"AI_TOKEN_ANNUAL_COST = 0.0 # $/yr from the Genesys quote — 0 reproduces the published study\n",
|
||||
"AI_TOKEN_QUOTE_NOTE = \"\" # e.g. \"Quote #1234: 4.2M tokens/yr @ $0.05, tier 2 commit\"\n",
|
||||
"\n",
|
||||
"print(f\"AI token line: ${AI_TOKEN_ANNUAL_COST:,.0f}/yr\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b7c6c4c7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Sensitivity — what the AI line does to NPV and ROI\n",
|
||||
"\n",
|
||||
"Computed locally from the current Athena summary: an annual cost `Δ` raises\n",
|
||||
"costs PV by `Δ × 2.4869` (the 3-year, 10% annuity factor) and lowers NPV by\n",
|
||||
"the same amount."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "09ceeea2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ANNUITY = sum(1 / 1.10**n for n in (1, 2, 3)) # 2.4869\n",
|
||||
"\n",
|
||||
"base_benefits_pv = float(summary[\"total_benefits_pv\"])\n",
|
||||
"base_costs_pv = float(summary[\"total_costs_pv\"])\n",
|
||||
"\n",
|
||||
"# Remove any token cost already stored, to get a clean zero-token base.\n",
|
||||
"current_tokens = next(\n",
|
||||
" (v for v in values if v[\"field_key\"] == \"genesys_ai_tokens\"), {})\n",
|
||||
"current_annual = (current_tokens.get(\"year_values\") or {}).get(\"1\", 0.0)\n",
|
||||
"base_costs_pv -= current_annual * ANNUITY\n",
|
||||
"\n",
|
||||
"sweep = [0, 100_000, 250_000, 500_000, 750_000, 1_000_000]\n",
|
||||
"if AI_TOKEN_ANNUAL_COST and AI_TOKEN_ANNUAL_COST not in sweep:\n",
|
||||
" sweep = sorted(sweep + [AI_TOKEN_ANNUAL_COST])\n",
|
||||
"\n",
|
||||
"rows = []\n",
|
||||
"for ai_annual in sweep:\n",
|
||||
" costs_pv = base_costs_pv + ai_annual * ANNUITY\n",
|
||||
" npv = base_benefits_pv - costs_pv\n",
|
||||
" rows.append({\n",
|
||||
" \"AI cost/yr\": f\"${ai_annual:,.0f}\" + (\" ← your input\" if ai_annual == AI_TOKEN_ANNUAL_COST and ai_annual else \"\"),\n",
|
||||
" \"Costs PV\": f\"${costs_pv:,.0f}\",\n",
|
||||
" \"NPV\": f\"${npv:,.0f}\",\n",
|
||||
" \"ROI\": f\"{npv / costs_pv * 100:,.0f}%\",\n",
|
||||
" })\n",
|
||||
"\n",
|
||||
"display(pd.DataFrame(rows))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6516270c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Push the AI token cost to Athena\n",
|
||||
"\n",
|
||||
"Writes the annual cost into `genesys_ai_tokens` (quote details in the notes),\n",
|
||||
"recalculates server-side, and saves a version."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c6caacea",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PUSH = False # ← set True once AI_TOKEN_ANNUAL_COST is final\n",
|
||||
"\n",
|
||||
"if PUSH:\n",
|
||||
" note = (\n",
|
||||
" f\"AI Experience tokens: ${AI_TOKEN_ANNUAL_COST:,.0f}/yr. \"\n",
|
||||
" + (f\"{AI_TOKEN_QUOTE_NOTE} \" if AI_TOKEN_QUOTE_NOTE else \"\")\n",
|
||||
" + \"Line absent from the published Forrester study.\"\n",
|
||||
" )\n",
|
||||
" client.update_values(TOOL_ID, [{\n",
|
||||
" \"field_key\": \"genesys_ai_tokens\",\n",
|
||||
" \"year_values\": {\"1\": round(AI_TOKEN_ANNUAL_COST, 2),\n",
|
||||
" \"2\": round(AI_TOKEN_ANNUAL_COST, 2),\n",
|
||||
" \"3\": round(AI_TOKEN_ANNUAL_COST, 2)},\n",
|
||||
" \"notes\": note,\n",
|
||||
" }])\n",
|
||||
" summary = client.calculate(TOOL_ID)\n",
|
||||
" client.print_summary(TOOL_ID)\n",
|
||||
" client.save_version(TOOL_ID, note=f\"AI token cost set: {note}\")\n",
|
||||
" print(\"✅ Pushed, recalculated, and versioned. Re-run the visualization \"\n",
|
||||
" \"cells above to refresh the charts.\")\n",
|
||||
"else:\n",
|
||||
" print(\"Dry run — set PUSH = True to write to Athena.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c261ad48",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"- Adjust other drivers for the client (interaction volume, agent count,\n",
|
||||
" self-service delta) via `client.update_values` or the Streamlit app\n",
|
||||
" (`make app`), saving a version per scenario.\n",
|
||||
"- Export charts for a deck: any figure object supports\n",
|
||||
" `fig.write_image(\"chart.png\")` (needs `pip install kaleido`) or\n",
|
||||
" `fig.write_html(\"chart.html\")`.\n",
|
||||
"- Export for the report pipeline:\n",
|
||||
" `python -m palladium export $PALLADIUM_GENESYSCX_TOOL_PUBLIC_ID -o exports/export.json`"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
1508
studies/202512_GenesysCX/notebooks/02_costs.ipynb
Normal file
1508
studies/202512_GenesysCX/notebooks/02_costs.ipynb
Normal file
File diff suppressed because one or more lines are too long
382
studies/202512_GenesysCX/notebooks/03_business_case.ipynb
Normal file
382
studies/202512_GenesysCX/notebooks/03_business_case.ipynb
Normal file
@@ -0,0 +1,382 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-intro",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 03 \u2014 Business Case\n",
|
||||
"\n",
|
||||
"Combine the benefits and costs into the consolidated TEI summary,\n",
|
||||
"render the cash-flow exhibit, run scenario analysis, **and price the\n",
|
||||
"Genesys AI Experience tokens line that the published study omits**.\n",
|
||||
"This notebook should reproduce the headline numbers from the PDF\n",
|
||||
"Financial Summary:\n",
|
||||
"\n",
|
||||
"* **NPV \\$10.78M \u2022 ROI 266% \u2022 Payback \u2248 4 months**\n",
|
||||
"\n",
|
||||
"It then exposes a sensitivity sweep for the AI-tokens annual cost so\n",
|
||||
"you can see exactly what an honest deal looks like before sending it\n",
|
||||
"to a client."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "03-bootstrap",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib # path shim: works on a fresh kernel\n",
|
||||
"for _p in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (_p / \"pyproject.toml\").exists():\n",
|
||||
" sys.path.insert(0, str(_p)); break\n",
|
||||
"\n",
|
||||
"from core.bootstrap import init\n",
|
||||
"\n",
|
||||
"pal = init(study=\"202512_GenesysCX\")\n",
|
||||
"client, seed, config = pal.client, pal.seed_data, pal.config\n",
|
||||
"\n",
|
||||
"STUDY = pal.root / 'studies' / '202512_GenesysCX'\n",
|
||||
"ROOT = pal.root\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-imports",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from core.export.report_data import _compute_summary\n",
|
||||
"from core.notebook_helpers import charts, display, tables"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-summary",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Local summary (no Athena round-trip)\n",
|
||||
"\n",
|
||||
"Compute the moderate-case TEI summary directly from `seed_data` so the\n",
|
||||
"notebook produces results even before the Athena tool is provisioned.\n",
|
||||
"Headline numbers should match the published study."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-summary",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"summary = _compute_summary(\n",
|
||||
" seed.BENEFITS,\n",
|
||||
" seed.COSTS,\n",
|
||||
" config.DISCOUNT_RATE,\n",
|
||||
" config.ANALYSIS_YEARS,\n",
|
||||
")\n",
|
||||
"# `_compute_summary` returns roi_pct; expose it as `roi` for kpi_cards.\n",
|
||||
"summary['roi'] = summary.get('roi_pct')\n",
|
||||
"display.kpi_cards(summary, title='Forrester composite \u2014 moderate case')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-cashflow-table",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"df_cash = tables.cashflow_table(summary)\n",
|
||||
"df_cash.style.format({c: '${:,.0f}' for c in df_cash.columns if c != 'Year'})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-cashflow",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Cash flow chart\n",
|
||||
"\n",
|
||||
"Mirrors the Forrester *Cash Flow Chart* exhibit: stacked benefits/costs\n",
|
||||
"by year + cumulative-net line. Payback hits inside Year 1."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-cashflow-chart",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"charts.cashflow_chart(\n",
|
||||
" summary['yearly_breakdown'],\n",
|
||||
" initial_cost=summary.get('initial_costs', 0),\n",
|
||||
").show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-waterfall",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Waterfall: Benefits PV \u2192 Costs PV \u2192 NPV"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-waterfall",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"charts.waterfall([\n",
|
||||
" ('Benefits PV', summary['total_benefits_pv']),\n",
|
||||
" ('Costs PV', -summary['total_costs_pv']),\n",
|
||||
" ('NPV', summary['npv']),\n",
|
||||
"]).show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-scenarios",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Scenario analysis\n",
|
||||
"\n",
|
||||
"Apply the default Palladium multipliers (see `core.calculations.SCENARIOS`):\n",
|
||||
"\n",
|
||||
"* **Conservative** \u2014 lower adoption, higher risk on benefits / lower on costs\n",
|
||||
"* **Moderate** \u2014 base case (= the published Forrester study)\n",
|
||||
"* **Aggressive** \u2014 full adoption, lower risk on benefits / higher on costs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-scenarios",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from core.calculations import apply_scenario\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"scenario_summaries = {}\n",
|
||||
"for name in ('conservative', 'moderate', 'aggressive'):\n",
|
||||
" sb = apply_scenario(seed.BENEFITS, name, table='benefits')\n",
|
||||
" sc = apply_scenario(seed.COSTS, name, table='costs')\n",
|
||||
" scenario_summaries[name] = _compute_summary(sb, sc, config.DISCOUNT_RATE, config.ANALYSIS_YEARS)\n",
|
||||
"\n",
|
||||
"scen_df = pd.DataFrame([\n",
|
||||
" {\n",
|
||||
" 'Scenario': k,\n",
|
||||
" 'Benefits PV': v['total_benefits_pv'],\n",
|
||||
" 'Costs PV': v['total_costs_pv'],\n",
|
||||
" 'NPV': v['npv'],\n",
|
||||
" 'ROI %': v['roi_pct'],\n",
|
||||
" 'Payback (mo)': round(v['payback_months'], 1) if v['payback_months'] is not None else None,\n",
|
||||
" }\n",
|
||||
" for k, v in scenario_summaries.items()\n",
|
||||
"])\n",
|
||||
"scen_df.style.format({\n",
|
||||
" 'Benefits PV': '${:,.0f}', 'Costs PV': '${:,.0f}', 'NPV': '${:,.0f}', 'ROI %': '{:,.0f}%'\n",
|
||||
"})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-scenario-chart",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"charts.scenario_comparison(scenario_summaries).show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-tokens-intro",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Genesys AI Experience tokens \u2014 annual cost\n",
|
||||
"\n",
|
||||
"Token pricing is tiered, capability-dependent, and deal-specific \u2014\n",
|
||||
"Athena stores a single annual cost value per line, and so does the\n",
|
||||
"seed. Enter the negotiated annual cost from the Genesys quote here.\n",
|
||||
"Quote details (volume, unit price, tier) go into the field notes for\n",
|
||||
"the audit trail.\n",
|
||||
"\n",
|
||||
"For sizing context, the study's own drivers imply roughly **1,040,000**\n",
|
||||
"self-service interactions/yr and **3,120,000** agent-assisted\n",
|
||||
"interactions/yr would draw tokens \u2014 bring the actual figure from the\n",
|
||||
"quote, not a derivation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-token-input",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# \u2500\u2500 Deal inputs \u2500\u2500\n",
|
||||
"AI_TOKEN_ANNUAL_COST = 0.0 # $/yr from the Genesys quote \u2014 0 reproduces the published study\n",
|
||||
"AI_TOKEN_QUOTE_NOTE = \"\" # e.g. \"Quote #1234: 4.2M tokens/yr @ $0.05, tier 2 commit\"\n",
|
||||
"\n",
|
||||
"print(f'AI token line: ${AI_TOKEN_ANNUAL_COST:,.0f}/yr')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-sensitivity",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Sensitivity \u2014 what the AI line does to NPV and ROI\n",
|
||||
"\n",
|
||||
"An annual cost `\u0394` raises Costs PV by `\u0394 \u00d7 2.4869` (the 3-year, 10%\n",
|
||||
"annuity factor) and lowers NPV by the same amount. The sweep below\n",
|
||||
"shows where the deal stops being attractive \u2014 and quantifies how much\n",
|
||||
"of the published 266% ROI was *contingent on Forrester modelling \\$0\n",
|
||||
"of token spend*."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-sensitivity",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ANNUITY = sum(1 / 1.10**n for n in (1, 2, 3)) # 2.4869\n",
|
||||
"\n",
|
||||
"base_benefits_pv = float(summary['total_benefits_pv'])\n",
|
||||
"base_costs_pv = float(summary['total_costs_pv'])\n",
|
||||
"\n",
|
||||
"sweep = [0, 100_000, 250_000, 500_000, 750_000, 1_000_000, 1_500_000, 2_000_000]\n",
|
||||
"if AI_TOKEN_ANNUAL_COST and AI_TOKEN_ANNUAL_COST not in sweep:\n",
|
||||
" sweep = sorted(sweep + [AI_TOKEN_ANNUAL_COST])\n",
|
||||
"\n",
|
||||
"rows = []\n",
|
||||
"for ai_annual in sweep:\n",
|
||||
" costs_pv = base_costs_pv + ai_annual * ANNUITY\n",
|
||||
" npv_v = base_benefits_pv - costs_pv\n",
|
||||
" roi_pct = (npv_v / costs_pv * 100) if costs_pv else 0\n",
|
||||
" rows.append({\n",
|
||||
" 'AI cost/yr': f\"${ai_annual:,.0f}\" + (' \u2190 your input' if ai_annual == AI_TOKEN_ANNUAL_COST and ai_annual else ''),\n",
|
||||
" 'Costs PV': f'${costs_pv:,.0f}',\n",
|
||||
" 'NPV': f'${npv_v:,.0f}',\n",
|
||||
" 'ROI': f'{roi_pct:,.0f}%',\n",
|
||||
" })\n",
|
||||
"\n",
|
||||
"pd.DataFrame(rows)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-tokens-push",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Push the AI-tokens cost to Athena\n",
|
||||
"\n",
|
||||
"When `AI_TOKEN_ANNUAL_COST` is set and `TOOL_PUBLIC_ID` exists, write\n",
|
||||
"the annual cost into the `genesys_ai_tokens` field, with the quote\n",
|
||||
"details preserved in the field notes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-tokens-push",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PUSH = False # \u2190 set True once AI_TOKEN_ANNUAL_COST is final\n",
|
||||
"\n",
|
||||
"if PUSH and config.TOOL_PUBLIC_ID:\n",
|
||||
" from core.tei_client import TEIClient\n",
|
||||
"\n",
|
||||
" note = (\n",
|
||||
" f'AI Experience tokens: ${AI_TOKEN_ANNUAL_COST:,.0f}/yr. '\n",
|
||||
" + (f'{AI_TOKEN_QUOTE_NOTE} ' if AI_TOKEN_QUOTE_NOTE else '')\n",
|
||||
" + 'Line absent from the published Forrester study.'\n",
|
||||
" )\n",
|
||||
" client = TEIClient()\n",
|
||||
" client.update_values(config.TOOL_PUBLIC_ID, [{\n",
|
||||
" 'field_key': 'genesys_ai_tokens',\n",
|
||||
" 'year_values': {'1': round(AI_TOKEN_ANNUAL_COST, 2),\n",
|
||||
" '2': round(AI_TOKEN_ANNUAL_COST, 2),\n",
|
||||
" '3': round(AI_TOKEN_ANNUAL_COST, 2)},\n",
|
||||
" 'notes': note,\n",
|
||||
" }])\n",
|
||||
" client.calculate(config.TOOL_PUBLIC_ID)\n",
|
||||
" client.print_summary(config.TOOL_PUBLIC_ID)\n",
|
||||
" client.save_version(config.TOOL_PUBLIC_ID, note=f'AI token cost set: {note}')\n",
|
||||
" display.alert('Pushed, recalculated, and versioned.', 'success')\n",
|
||||
"else:\n",
|
||||
" display.alert('Dry run \u2014 set <code>PUSH = True</code> and ensure '\n",
|
||||
" '<code>TOOL_PUBLIC_ID</code> is configured to write to Athena.', 'info')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-crosscheck",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Cross-check vs Athena (optional)\n",
|
||||
"\n",
|
||||
"When `TOOL_PUBLIC_ID` is set, ask Athena to recalculate the summary on\n",
|
||||
"the server side and confirm it matches our local computation (modulo\n",
|
||||
"the documented Year-0 discounting delta \u2014 see `02_costs.ipynb`)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g3-code-crosscheck",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if config.TOOL_PUBLIC_ID:\n",
|
||||
" from core.tei_client import TEIClient\n",
|
||||
"\n",
|
||||
" client = TEIClient()\n",
|
||||
" client.calculate(config.TOOL_PUBLIC_ID)\n",
|
||||
" server_summary = client.get_summary(config.TOOL_PUBLIC_ID)\n",
|
||||
" display.kpi_cards(server_summary, title='Athena server-side summary')\n",
|
||||
"else:\n",
|
||||
" display.alert('Set TOOL_PUBLIC_ID to compare Athena vs local.', 'info')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g3-md-next",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Continue with [`04_export.ipynb`](04_export.ipynb) \u2192"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
195
studies/202512_GenesysCX/notebooks/04_export.ipynb
Normal file
195
studies/202512_GenesysCX/notebooks/04_export.ipynb
Normal file
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g4-md-intro",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 04 \u2014 Export for the report pipeline\n",
|
||||
"\n",
|
||||
"Build the structured JSON envelope consumed by the html2docx report\n",
|
||||
"generation pipeline (Peitho). Output goes to `exports/export.json`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "04-bootstrap",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib # path shim: works on a fresh kernel\n",
|
||||
"for _p in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (_p / \"pyproject.toml\").exists():\n",
|
||||
" sys.path.insert(0, str(_p)); break\n",
|
||||
"\n",
|
||||
"from core.bootstrap import init\n",
|
||||
"\n",
|
||||
"pal = init(study=\"202512_GenesysCX\")\n",
|
||||
"client, seed, config = pal.client, pal.seed_data, pal.config\n",
|
||||
"\n",
|
||||
"STUDY = pal.root / 'studies' / '202512_GenesysCX'\n",
|
||||
"ROOT = pal.root\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g4-code-imports",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from datetime import datetime, timezone\n",
|
||||
"from core import __version__\n",
|
||||
"from core.calculations import apply_scenario\n",
|
||||
"from core.export.report_data import _compute_summary\n",
|
||||
"from core.notebook_helpers import display"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g4-md-build",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Build the envelope\n",
|
||||
"\n",
|
||||
"Two paths:\n",
|
||||
"\n",
|
||||
"* **Live** \u2014 `core.export.build_report_data(client, public_id)` pulls\n",
|
||||
" authoritative values + summary from Athena and stamps it.\n",
|
||||
"* **Local** \u2014 when no `TOOL_PUBLIC_ID` is configured, build the envelope\n",
|
||||
" directly from `seed_data` so this notebook is always runnable."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g4-code-build",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if config.TOOL_PUBLIC_ID:\n",
|
||||
" from core.export import build_report_data\n",
|
||||
" from core.tei_client import TEIClient\n",
|
||||
"\n",
|
||||
" client = TEIClient()\n",
|
||||
" envelope = build_report_data(\n",
|
||||
" client,\n",
|
||||
" config.TOOL_PUBLIC_ID,\n",
|
||||
" include_scenarios=True,\n",
|
||||
" study_slug=config.STUDY_SLUG,\n",
|
||||
" )\n",
|
||||
" source = 'live (Athena)'\n",
|
||||
"else:\n",
|
||||
" summary = _compute_summary(\n",
|
||||
" seed.BENEFITS, seed.COSTS, config.DISCOUNT_RATE, config.ANALYSIS_YEARS\n",
|
||||
" )\n",
|
||||
" summary['roi'] = summary.get('roi_pct')\n",
|
||||
" scenarios = {}\n",
|
||||
" for name in ('conservative', 'moderate', 'aggressive'):\n",
|
||||
" sb = apply_scenario(seed.BENEFITS, name, table='benefits')\n",
|
||||
" sc = apply_scenario(seed.COSTS, name, table='costs')\n",
|
||||
" scenarios[name] = _compute_summary(sb, sc, config.DISCOUNT_RATE, config.ANALYSIS_YEARS)\n",
|
||||
" envelope = {\n",
|
||||
" 'metadata': {\n",
|
||||
" 'study_slug': config.STUDY_SLUG,\n",
|
||||
" 'tool_public_id': '',\n",
|
||||
" 'tool_name': 'CX Cloud (Genesys + Salesforce) TEI (local seed)',\n",
|
||||
" 'report_name': 'Total Economic Impact\u2122 Of CX Cloud \u2014 Genesys + Salesforce',\n",
|
||||
" 'report_vendor': 'Genesys',\n",
|
||||
" 'report_version': '1.0',\n",
|
||||
" 'generated_at': datetime.now(timezone.utc).isoformat(),\n",
|
||||
" 'generator': f'palladium core {__version__} (offline)',\n",
|
||||
" },\n",
|
||||
" 'report': {\n",
|
||||
" 'name': 'Total Economic Impact\u2122 Of CX Cloud \u2014 Genesys + Salesforce',\n",
|
||||
" 'vendor': 'Genesys',\n",
|
||||
" 'version': '1.0',\n",
|
||||
" 'discount_rate': config.DISCOUNT_RATE,\n",
|
||||
" 'analysis_period_years': config.ANALYSIS_YEARS,\n",
|
||||
" },\n",
|
||||
" 'values': {'benefits': seed.BENEFITS, 'costs': seed.COSTS},\n",
|
||||
" 'summary': summary,\n",
|
||||
" 'scenarios': scenarios,\n",
|
||||
" 'assumptions': seed.ASSUMPTIONS,\n",
|
||||
" }\n",
|
||||
" source = 'offline seed data'\n",
|
||||
"\n",
|
||||
"display.alert(f'Envelope built from <b>{source}</b>.', 'info')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g4-code-write",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"out_path = STUDY / 'exports' / 'export.json'\n",
|
||||
"out_path.parent.mkdir(parents=True, exist_ok=True)\n",
|
||||
"out_path.write_text(json.dumps(envelope, indent=2, default=str))\n",
|
||||
"size_kb = out_path.stat().st_size / 1024\n",
|
||||
"display.alert(f'Wrote <code>{out_path.relative_to(ROOT)}</code> ({size_kb:.1f} KB).', 'success')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g4-md-shape",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Envelope shape\n",
|
||||
"\n",
|
||||
"Top-level keys consumed by the report pipeline:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "g4-code-shape",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for key in envelope:\n",
|
||||
" sub = envelope[key]\n",
|
||||
" if isinstance(sub, dict):\n",
|
||||
" print(f' {key}: dict with keys {list(sub.keys())}')\n",
|
||||
" elif isinstance(sub, list):\n",
|
||||
" print(f' {key}: list[{len(sub)}]')\n",
|
||||
" else:\n",
|
||||
" print(f' {key}: {type(sub).__name__}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "g4-md-done",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Done. Hand off `exports/export.json` to **Peitho** / **html2docx** to produce the final Word report.\n",
|
||||
"\n",
|
||||
"**CLI alternative:** `python -m palladium export $PALLADIUM_GENESYSCX_TOOL_PUBLIC_ID -o studies/202512_GenesysCX/exports/export.json`"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user