Migrate Genesys CX Cloud TEI study to the pattern; retire Streamlit app
studies/202512_GenesysCX -> studies/202512_TEI_Genesys_CX_Cloud, rebuilt as pattern Variant 4 (TEI composite reproduction): - teicalc/ self-contained engine: Forrester's tables as the never-edited verbatim anchor (incl. the p.14 typo note and the $0 AI-token line), generic model/scenarios/staging carried over from the Amazon Connect study, ClientDrivers overlay (agents / weekly interactions / revenue, flat composite so no growth re-base) with ai_tokens_annual as a direct input for the token line the published study models at $0 - one deliverable notebook (business_case.ipynb): widget-pair sidebar drivers incl. the AI-token price, published-vs-overlay KPI columns, cash-flow/waterfall/scenario charts, verification gate, backstage JSON data appendix - gate + tests reproduce the published totals within $2: NPV $10.8M / ROI 266% (engine $10,783,466 / 265.79%; payback 3.3 months, not headlined in the PDF); 29 study tests, headless nbconvert green, stage simulation leak-free, exports carry the appendix - old Athena workflow (00_provision..04_export, config.py, seed_data.py, PALLADIUM_GENESYSCX_* keys, ATHENA_EXPECTED reconciliation) deleted; git history preserves it With the last legacy study migrated, the retirement lands too: - app/ (Streamlit UI) and core/notebook_helpers deleted; nothing else imported them - streamlit stripped from pyproject extras, requirements.txt, Makefile; .env.example reduced to the Athena keys; 00_setup.ipynb and core/bootstrap.py repointed at the pattern studies - root README reworked: self-contained studies + slim core/ Athena toolkit (tei_client, calculations, export, cli) All suites green: Genesys 29, Amazon Connect 27, CTM 55, template 7, root 58. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
108
00_setup.ipynb
108
00_setup.ipynb
@@ -4,21 +4,7 @@
|
||||
"cell_type": "markdown",
|
||||
"id": "021ac129",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🛡️ Palladium — Setup & Connection\n",
|
||||
"\n",
|
||||
"**Start here.** This notebook gets you from a fresh clone to a working Athena connection.\n",
|
||||
"\n",
|
||||
"| Where things live | |\n",
|
||||
"|---|---|\n",
|
||||
"| `00_setup.ipynb` | ← you are here: credentials + connection check |\n",
|
||||
"| `studies/<slug>/notebooks/` | the actual TEI work, numbered `00_provision` → `04_export` |\n",
|
||||
"| `core/` | shared logic (API client, financial math) — you rarely edit this |\n",
|
||||
"| `app/` | Streamlit data-entry UI: `make app` or `streamlit run app/main.py` |\n",
|
||||
"| `.env` | your Athena URL + API key (gitignored; created below) |\n",
|
||||
"\n",
|
||||
"Run cells top to bottom. Re-run any time — every step is idempotent."
|
||||
]
|
||||
"source": "# 🛡️ Palladium — Setup & Connection\n\n**Start here.** This notebook gets you from a fresh clone to a working Athena connection.\n\n| Where things live | |\n|---|---|\n| `00_setup.ipynb` | ← you are here: credentials + connection check |\n| `studies/<slug>/` | self-contained pattern studies (own venv, engine, Mercury notebook) |\n| `core/` | shared logic (API client, financial math) — you rarely edit this |\n| `.env` | your Athena URL + API key (gitignored; created below) |\n\nRun cells top to bottom. Re-run any time — every step is idempotent."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -37,32 +23,13 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Bootstrap — finds the repo root, loads .env, builds the API client.\n",
|
||||
"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, save_credentials\n",
|
||||
"\n",
|
||||
"pal = init(connect=False)\n",
|
||||
"pal"
|
||||
]
|
||||
"source": "# Bootstrap — finds the repo root, loads .env, builds the API client.\nimport sys, pathlib # path shim: works on a fresh kernel\nfor _p in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n if (_p / \"pyproject.toml\").exists():\n sys.path.insert(0, str(_p)); break\n\nfrom core.bootstrap import init, save_credentials\n\npal = init(connect=False)\npal"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7ca43976",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1 · Credentials\n",
|
||||
"\n",
|
||||
"Stored in `<repo>/.env` (gitignored). The cell below only prompts if no key is\n",
|
||||
"configured yet — paste the key at the prompt and it's saved for every future\n",
|
||||
"session, notebook, the CLI, and the Streamlit app.\n",
|
||||
"\n",
|
||||
"Current target: **https://athena.ouranos.helu.ca** (Ouranos sandbox — safe to experiment, no production data)."
|
||||
]
|
||||
"source": "## 1 · Credentials\n\nStored in `<repo>/.env` (gitignored). The cell below only prompts if no key is\nconfigured yet — paste the key at the prompt and it's saved for every future\nsession, notebook, the CLI, and the Streamlit app.\n\nCurrent target: **https://athena.ouranos.helu.ca** (Ouranos sandbox — safe to experiment, no production data)."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -79,26 +46,13 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from getpass import getpass\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"ATHENA_API_KEY\"):\n",
|
||||
" key = getpass(\"Athena API key (input hidden): \")\n",
|
||||
" path = save_credentials(api_key=key)\n",
|
||||
" print(f\"Saved → {path}\")\n",
|
||||
"else:\n",
|
||||
" print(f\"✅ Credentials already configured for {os.getenv('ATHENA_BASE_URL')}\")\n",
|
||||
" print(\" (To rotate the key: save_credentials(api_key='new-key'))\")"
|
||||
]
|
||||
"source": "import os\nfrom getpass import getpass\n\nif not os.getenv(\"ATHENA_API_KEY\"):\n key = getpass(\"Athena API key (input hidden): \")\n path = save_credentials(api_key=key)\n print(f\"Saved → {path}\")\nelse:\n print(f\"✅ Credentials already configured for {os.getenv('ATHENA_BASE_URL')}\")\n print(\" (To rotate the key: save_credentials(api_key='new-key'))\")"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "aa7464fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2 · Test the connection"
|
||||
]
|
||||
"source": "## 2 · Test the connection"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -128,19 +82,13 @@
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"pal = init() # builds the client and pings /api/v1/tei/reports/\n",
|
||||
"client = pal.client\n",
|
||||
"pal.connection"
|
||||
]
|
||||
"source": "pal = init() # builds the client and pings /api/v1/tei/reports/\nclient = pal.client\npal.connection"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6877d6ae",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3 · What's in this Athena instance?"
|
||||
]
|
||||
"source": "## 3 · What's in this Athena instance?"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -209,19 +157,7 @@
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"reports = client.list_reports()\n",
|
||||
"if reports:\n",
|
||||
" display(pd.DataFrame(reports)[\n",
|
||||
" [c for c in (\"id\", \"name\", \"vendor\", \"version\", \"status\",\n",
|
||||
" \"analysis_period_years\", \"discount_rate\",\n",
|
||||
" \"field_count\", \"instance_count\") if c in reports[0]]\n",
|
||||
" ])\n",
|
||||
"else:\n",
|
||||
" print(\"No TEI report templates yet — studies/202602_AmazonConnect/notebooks/00_provision.ipynb creates one.\")"
|
||||
]
|
||||
"source": "import pandas as pd\n\nreports = client.list_reports()\nif reports:\n display(pd.DataFrame(reports)[\n [c for c in (\"id\", \"name\", \"vendor\", \"version\", \"status\",\n \"analysis_period_years\", \"discount_rate\",\n \"field_count\", \"instance_count\") if c in reports[0]]\n ])\nelse:\n print(\"No TEI report templates yet.\")"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -237,31 +173,13 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tools = client.list_tools()\n",
|
||||
"if tools:\n",
|
||||
" display(pd.DataFrame(tools)[\n",
|
||||
" [c for c in (\"id\", \"name\", \"status\", \"current_version\") if c in tools[0]]\n",
|
||||
" ])\n",
|
||||
"else:\n",
|
||||
" print(\"No TEI tool instances yet.\")"
|
||||
]
|
||||
"source": "tools = client.list_tools()\nif tools:\n display(pd.DataFrame(tools)[\n [c for c in (\"id\", \"name\", \"status\", \"current_version\") if c in tools[0]]\n ])\nelse:\n print(\"No TEI tool instances yet.\")"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "33114d67",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"1. **Provision the Amazon Connect study** → open\n",
|
||||
" [`studies/202602_AmazonConnect/notebooks/00_provision.ipynb`](studies/202602_AmazonConnect/notebooks/00_provision.ipynb).\n",
|
||||
" It creates the report template + fields in the sandbox, creates a tool,\n",
|
||||
" seeds the Forrester values, calculates, and verifies the published totals\n",
|
||||
" (NPV \\$78.7M · ROI 342% · payback <6 months).\n",
|
||||
"2. **Work the study** → notebooks `01_benefits` → `04_export` in the same folder.\n",
|
||||
"3. **Interactive data entry** → `make app` (or `streamlit run app/main.py`)."
|
||||
]
|
||||
"source": "## Next steps\n\n1. **Open a study** — each `studies/<slug>/` is self-contained (own venv +\n engine + verification gate): see its README, e.g.\n [`studies/202602_TEI_Amazon_Connect/`](studies/202602_TEI_Amazon_Connect/README.md)\n (NPV \\$78.7M · ROI 342%) or\n [`studies/202512_TEI_Genesys_CX_Cloud/`](studies/202512_TEI_Genesys_CX_Cloud/README.md)\n (NPV \\$10.8M · ROI 266%).\n2. **Serve a deliverable** → `mercury --working-dir notebooks/` from the study root.\n3. **Start a new study** → copy `template/MercuryNotebook/` per the\n [pattern](docs/Mercury_Notebook_Pattern_V1-00.md)."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -269,7 +187,7 @@
|
||||
"id": "d20d824f-e464-4ff7-8191-10c2495842a0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": ""
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -277,7 +195,7 @@
|
||||
"id": "630ee935-7c7b-47e5-9c13-6285316823e2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": ""
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
@@ -285,7 +203,7 @@
|
||||
"id": "7eba3877-8e51-443f-9953-9d0a48425f9f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
"source": ""
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
Reference in New Issue
Block a user