{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "38ca63e6",
   "metadata": {},
   "source": [
    "# Creating Calculation Transforms\n",
    "\n",
    "This notebook provides a step-by-step guide to set up, test, and deploy Calculation models using Falkonry APIs. Follow each section to create a workspace, define calculation, evaluate them, set up assessments, and monitor live outputs.\n",
    "\n",
    "### 11 Steps to go live with your custom Calculations\n",
    "1. Set Up Falkonry API Access\n",
    "2. Create Workspace\n",
    "3. Test the calculation function with sample data before creating the transform\n",
    "4. Convert the calculate function in to string\n",
    "5. Create a calculated model\n",
    "6. Run CALCEVAL for Testing\n",
    "7. Create Assessment\n",
    "8. Get Assessment Info and Extract Output Signals\n",
    "9. Run CALCEVAL for Live Assessment Output\n",
    "10. Stop monitoring an assessment\n",
    "11. Restart monitoring an assessment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f53fa532",
   "metadata": {},
   "source": [
    "COPYRIGHT (c) 2026 FALKONRY, INC. ALL RIGHTS RESERVED.\n",
    "\n",
    "FOR FALKONRY's CUSTOMER USE ONLY.\n",
    "\n",
    "THIS SAMPLE CODE FROM FALKONRY IS PROVIDED \"AS IS\" AND WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FALKONRY INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) SUSTAINED BY YOU OR A THIRD PARTY, HOWEVER CAUSED AND UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ARISING IN ANY WAY OUT OF THE USE OF OR INABILITY TO USE THIS SAMPLE CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29b89e3a",
   "metadata": {},
   "source": [
    "## 1. Set Up Falkonry API Access\n",
    "\n",
    "Configure authentication and base URL for Falkonry API requests. Set your API key, account_id, and other required variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6a8c728",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Testing API connection...\n",
      "https://app3.falkonry.ai/api/1.3/accounts/1356886656271327232\n",
      "Status code: 200\n",
      "API connection successful!\n",
      "Proceed with the rest of the notebook...\n"
     ]
    }
   ],
   "source": [
    "# Set up Falkonry API access\n",
    "import requests\n",
    "from asyncio import sleep\n",
    "\n",
    "# Set your API connection and account ID here\n",
    "BASE_SERVER = \"\" # e.g., app3.falkonry.ai\n",
    "API_TOKEN   = \"\" # <YOUR_API_TOKEN>\n",
    "ACCOUNT_ID  = \"\" # <YOUR_ACCOUNT_ID>\n",
    "BASE_URL    = f\"https://{BASE_SERVER}/api/1.3\"\n",
    "HEADERS     = {\n",
    "    \"Authorization\": f\"Bearer {API_TOKEN}\",\n",
    "    \"Content-Type\": \"application/json\"\n",
    "}\n",
    "\n",
    "# Set your workspace, anomaly model and assessment parameters here\n",
    "WORKSPACE_ID = \"\" # e.g., \"1356886815950516129\"\n",
    "WORKSPACE_NAME = \"Calculation Transforms with API v1.3\" # e.g., \"Machine 2 Model Development\"\n",
    "TREE_NAME = \"\" # e.g., Assets\n",
    "NODE_NAME = \"\" # e.g., 1356886129815950516\n",
    "\n",
    "# Naming convention: <calculated_signal_name>/calculated\n",
    "MODEL_NAME = f\"\" # e.g., M1_Temp_Torque_Ratio/calculated \n",
    "\n",
    "MODEL_NAME_SUFFIX = \"01\" # e.g., \"01\"\n",
    "EVAL_TIME_RANGE = {\n",
    "        \"startTime\": \"\", # e.g., \"2024-07-22T23:01:09.000Z\", \n",
    "        \"endTime\": \"\" # e.g., \"2024-08-01T23:01:09.000Z\"\n",
    "}\n",
    "INPUT_SIGNALS = [\n",
    "      { \"signal\": \"\", \"name\": \"\" }, # e.g., { \"signal\": \"189245342354643574\", \"name\": \"temperature\" },\n",
    "      { \"signal\": \"\", \"name\": \"\"} # e.g., { \"signal\": \"189876234785236753\", \"name\": \"torque\" },\n",
    "    ]\n",
    "OUTPUT_PREFIX = \"test01/\" # e.g., \"test01/\"\n",
    "\n",
    "# Test the API connection before proceeding with the rest of the notebook\n",
    "print(\"Testing API connection...\")\n",
    "try:\n",
    "    test_url = f\"{BASE_URL}/accounts/{ACCOUNT_ID}\"\n",
    "    print (test_url)\n",
    "    response = requests.get(test_url, headers=HEADERS, timeout=10)\n",
    "    print(f\"Status code: {response.status_code}\")\n",
    "    if response.status_code == 200:\n",
    "        print(\"API connection successful!\")\n",
    "        print(\"Proceed with the rest of the notebook...\")\n",
    "    else:\n",
    "        print(f\"API error: {response.text[:100]}\")\n",
    "        print(\"STOP! Check your API connection parameters and try again.\")\n",
    "except Exception as e:\n",
    "    print(f\"Connection error: {str(e)}\")\n",
    "    print(\"STOP! Check your API connection parameters and try again.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2865181",
   "metadata": {},
   "source": [
    "## 2. Create Workspace\n",
    "\n",
    "Send a POST request to `/api/1.3/accounts/{{account_id}}/workspace` to create a new workspace for development. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a6e2edf2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Status: 201\n",
      "Response: {'id': '1513998823717732352', 'tenant': '1356886656271327232', 'type': 'entities.workspace', 'name': 'Calculation Transforms with API v1.3', 'description': 'Created by notebook', 'createTime': 1781035848312, 'updateTime': 1781035848312, 'createdBy': '597843765695430656', 'updatedBy': '597843765695430656', 'archived': False, 'links': []}\n",
      "Created Workspace ID: 1513998823717732352\n"
     ]
    }
   ],
   "source": [
    "if WORKSPACE_ID:\n",
    "    workspace_id = WORKSPACE_ID\n",
    "    print(\"Using the Existing Workspace ID:\", workspace_id)\n",
    "else:\n",
    "    # Create a new workspace\n",
    "    workspace_payload = {\n",
    "        \"name\": WORKSPACE_NAME,\n",
    "        \"description\": \"Created by notebook\"\n",
    "    }\n",
    "\n",
    "    response = requests.post(\n",
    "        f\"{BASE_URL}/accounts/{ACCOUNT_ID}/workspace\",\n",
    "        headers=HEADERS,\n",
    "        json=workspace_payload\n",
    "    )\n",
    "\n",
    "    print(\"Status:\", response.status_code)\n",
    "    print(\"Response:\", response.json())\n",
    "\n",
    "    workspace_id = response.json().get(\"id\")\n",
    "    print(\"Created Workspace ID:\", workspace_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "649a6135",
   "metadata": {},
   "source": [
    "## 3. Test the calculation function with sample data before creating the transform\n",
    "Prepare the function to setup the calculation model. Function should include the inputschema and outputschema for the model. \n",
    "\n",
    "Import the python libraries such as numpy within the function itself. Do not use aliases for the libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e5156121",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO: Resulting Ratios: [0.5  0.29  nan 1.33 0.31 0.46]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'heat_ratio': [0.5, 0.29, nan, 1.33, 0.31, 0.46]}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#Test the calculation function with sample data before creating the transform\n",
    "input_schema = [\n",
    "    {\"name\": \"temperature\", \"valueType\": \"Numeric\"},\n",
    "    {\"name\": \"torque\", \"valueType\": \"Numeric\"}\n",
    "]\n",
    "\n",
    "output_schema = [\n",
    "    {\"name\": \"heat_ratio\", \"valueType\": \"Numeric\"}\n",
    "]\n",
    "\n",
    "def calculate(signal_data):\n",
    "    import numpy\n",
    "\n",
    "    temperature = numpy.array(signal_data.get(\"temperature\", []), dtype=float)\n",
    "    torque = numpy.array(signal_data.get(\"torque\", []), dtype=float)\n",
    "    condition = (torque != 0) & (numpy.isfinite(torque)) & (numpy.isfinite(temperature))\n",
    "    \n",
    "    with numpy.errstate(divide='ignore', invalid='ignore'):\n",
    "        ratios = numpy.divide(temperature, torque, out=numpy.full_like(temperature, numpy.nan), where=condition)\n",
    "    \n",
    "    ratios_rounded = numpy.round(ratios, 2)\n",
    "    \n",
    "    print(f\"INFO: Resulting Ratios: {ratios_rounded}\")\n",
    "    \n",
    "    return {\"heat_ratio\": ratios_rounded.tolist()}\n",
    "\n",
    "# test the calculation function with \n",
    "# sample data before creating the transform\n",
    "calculate({\"temperature\": [1,2,3,4,5,6], \"torque\": [2,7,0,3,16,13]})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "544434ce",
   "metadata": {},
   "source": [
    "## 4. Convert the calculate function in to string\n",
    "Now that the `calculate ()` function is tested OK, convert the function to a string for API submission"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "3583bf00",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Convert the calculation function to a string for API submission\n",
    "calc_script = \"\"\"\n",
    "input_schema = [\n",
    "    {\"name\": \"temperature\", \"valueType\": \"Numeric\"},\n",
    "    {\"name\": \"torque\", \"valueType\": \"Numeric\"}\n",
    "]\n",
    "\n",
    "output_schema = [\n",
    "    {\"name\": \"heat_ratio\", \"valueType\": \"Numeric\"}\n",
    "]\n",
    "\n",
    "def calculate(signal_data):\n",
    "    import numpy\n",
    "\n",
    "    temperature = numpy.array(signal_data.get(\"temperature\", []), dtype=float)\n",
    "    torque = numpy.array(signal_data.get(\"torque\", []), dtype=float)\n",
    "    condition = (torque != 0) & (numpy.isfinite(torque)) & (numpy.isfinite(temperature))\n",
    "    \n",
    "    with numpy.errstate(divide='ignore', invalid='ignore'):\n",
    "        ratios = numpy.divide(temperature, torque, out=numpy.full_like(temperature, numpy.nan), where=condition)\n",
    "    \n",
    "    ratios_rounded = numpy.round(ratios, 2)\n",
    "    \n",
    "    print(f\"INFO: Resulting Ratios: {ratios_rounded}\")\n",
    "    \n",
    "    return {\"heat_ratio\": ratios_rounded.tolist()}\n",
    "\"\"\".strip()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c10747d9",
   "metadata": {},
   "source": [
    "## 5. Create a calculated model\n",
    "\n",
    "Send a POST request to `/api/1.3/accounts/{{account_id}}/flows` to create a new calculated model in the development workspace. Use the payload specifying python script, input schema, and model configuration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "1fe21503",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Status: 201\n",
      "MODELSETUP Flow ID: 1513999082183327744\n",
      "{'id': '1513999082183327744', 'type': 'entities.flow', 'tenant': '1356886656271327232', 'flowType': 'MODELSETUP', 'status': 'COMPLETED', 'name': 'Calc Transform - Temperature to Torque Ratio', 'spec': {'modelName': '', 'modelType': 'CALCULATED', 'modelDetails': {'statistic': 'mean', 'script': 'input_schema = [\\n    {\"name\": \"temperature\", \"valueType\": \"Numeric\"},\\n    {\"name\": \"torque\", \"valueType\": \"Numeric\"}\\n]\\n\\noutput_schema = [\\n    {\"name\": \"heat_ratio\", \"valueType\": \"Numeric\"}\\n]\\n\\ndef calculate(signal_data):\\n    import numpy\\n\\n    temperature = numpy.array(signal_data.get(\"temperature\", []), dtype=float)\\n    torque = numpy.array(signal_data.get(\"torque\", []), dtype=float)\\n    condition = (torque != 0) & (numpy.isfinite(torque)) & (numpy.isfinite(temperature))\\n\\n    with numpy.errstate(divide=\\'ignore\\', invalid=\\'ignore\\'):\\n        ratios = numpy.divide(temperature, torque, out=numpy.full_like(temperature, numpy.nan), where=condition)\\n\\n    ratios_rounded = numpy.round(ratios, 2)\\n\\n    print(f\"INFO: Resulting Ratios: {ratios_rounded}\")\\n\\n    return {\"heat_ratio\": ratios_rounded.tolist()}', 'valueType': 'Numeric', 'evaluationWindow': 'PT1M', 'clockSignals': [], 'assessmentRate': None, 'delayTolerance': None}, 'inputschema': [], 'workspace': '1513998823717732352', 'config': {}}, 'flowState': {'name': 'modelsetup', 'version': '1.3', 'stages': [{'id': '1.1', 'type': 'connector', 'name': 'start', 'description': 'Model Setup', 'parents': [], 'jobs': ['1513999082367877120'], 'status': 'COMPLETED', 'contextMap': {}, 'transformFunc': 'modelSetup'}, {'id': '2.1', 'type': 'connector', 'name': 'end', 'description': 'End Stage', 'parents': ['1.1'], 'jobs': [], 'status': 'COMPLETED', 'contextMap': {}}], 'jobs': [{'status': 'COMPLETED', 'id': '1513999082367877120'}]}, 'outputs': [{'jobType': 'MODELSETUP', 'model': '1513999082627923968', 'modelName': 'Calculation Transforms with API v1.3/CALCULATED/M[2]'}], 'messages': [], 'createTime': 1781035909935, 'updateTime': 1781035910164, 'createdBy': '597843765695430656', 'updatedBy': 'pod', 'archived': False, 'transitions': [{'id': '1', 'status': 'CREATED', 'createTime': '2026-06-09T20:11:49.935000Z'}, {'id': '2', 'status': 'RUNNING', 'createTime': '2026-06-09T20:11:49.960000Z'}, {'id': '3', 'status': 'COMPLETED', 'createTime': '2026-06-09T20:11:50.164000Z'}], 'links': []}\n",
      "Model ID: 1513999082627923968\n",
      "Input Schema: [{'name': 'temperature', 'valueType': 'Numeric'}, {'name': 'torque', 'valueType': 'Numeric'}]\n",
      "Output Schema: [{'name': 'heat_ratio', 'valueType': 'Numeric'}]\n"
     ]
    }
   ],
   "source": [
    "calc_payload = {\n",
    "  \"name\": \"Calc Transform - Temperature to Torque Ratio\",\n",
    "  \"flowType\": \"MODELSETUP\",\n",
    "  \"spec\": {\n",
    "    \"workspace\": workspace_id,\n",
    "    \"modelType\": \"CALCULATED\",\n",
    "    \"modelName\": MODEL_NAME,\n",
    "    \"modelDetails\": {\n",
    "      \"statistic\": MODEL_PARAMETERS_STATISTIC,\n",
    "      \"script\": calc_script,\n",
    "      \"valueType\": MODEL_PARAMETERS_VALUETYPE,\n",
    "      \"evaluationWindow\":\n",
    "    }\n",
    "  }\n",
    "}\n",
    "\n",
    "response = requests.post(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows\",\n",
    "    headers=HEADERS,\n",
    "    json=calc_payload\n",
    ")\n",
    "\n",
    "print(\"Status:\", response.status_code)\n",
    "flow_id = response.json().get(\"id\")\n",
    "print(\"MODELSETUP Flow ID:\", flow_id)\n",
    "\n",
    "# Wait for the Flow to be readyand then get the model ID from the created flow\n",
    "await sleep (10)\n",
    "#get the model ID from the created flow\n",
    "response = requests.get(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows/{flow_id}\",\n",
    "    headers=HEADERS)\n",
    "\n",
    "print(response.json())\n",
    "model_id = response.json()[\"outputs\"][0][\"model\"]\n",
    "print(\"Model ID:\", model_id)\n",
    "\n",
    "await sleep (10)\n",
    "# Get the model ID from the flow created above\n",
    "#\n",
    "# Send a GET request to `/models/{model_id}?denorm=inputschema&denorm=outputschema` \n",
    "# to get info on the input and output schema for the model. This is useful in debugging \n",
    "# failed CALCEVAL flows due to mismatch in the input schema and output schema.\n",
    "response = requests.get(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/models/{model_id}?denorm=inputschema&denorm=outputschema\",\n",
    "    headers=HEADERS)\n",
    "\n",
    "links = response.json()[\"links\"]\n",
    "input_schema = links[0][\"object\"][\"schema\"]\n",
    "\n",
    "print(\"Input Schema:\", input_schema)\n",
    "\n",
    "output_schema = links[1][\"object\"][\"schema\"]\n",
    "print(\"Output Schema:\", output_schema)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5827fff7",
   "metadata": {},
   "source": [
    "## 6. Run CALCEVAL for Testing\n",
    "\n",
    "Send a POST request to `/api/1.3/accounts/{{account_id}}/flows` with `flowType` 'CALCEVAL' and an `outputsignalPrefix` to test the rule on historical data without affecting assessment outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "c75ee2d7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"name\": \"eval for Calc Heat Ratio\",\n",
      "  \"description\": \"Evaluating the Heat Ratio on machine 1\",\n",
      "  \"flowType\": \"CALCEVAL\",\n",
      "  \"spec\": {\n",
      "    \"model\": \"1513999082627923968\",\n",
      "    \"workspace\": \"1513998823717732352\",\n",
      "    \"timeRange\": {\n",
      "      \"startTime\": \"2024-07-22T23:01:09.000Z\",\n",
      "      \"endTime\": \"2024-08-01T23:01:09.000Z\"\n",
      "    },\n",
      "    \"inputsignals\": [\n",
      "      {\n",
      "        \"signal\": \"1505769909337915392\",\n",
      "        \"name\": \"temperature\"\n",
      "      },\n",
      "      {\n",
      "        \"signal\": \"1505769905961500672\",\n",
      "        \"name\": \"torque\"\n",
      "      }\n",
      "    ],\n",
      "    \"outputsignalPrefix\": \"test01/\"\n",
      "  }\n",
      "}\n",
      "Status: 201\n",
      "CALCEVAL Flow ID: 1514000119053455360\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "calceval_payload = {\n",
    "  \"name\": \"eval for Calc Heat Ratio\",\n",
    "  \"description\": \"Evaluating the Heat Ratio on machine 1\",\n",
    "  \"flowType\": \"CALCEVAL\",\n",
    "  \"spec\": {\n",
    "    \"model\": model_id,\n",
    "    \"workspace\": workspace_id,\n",
    "    \"timeRange\": EVAL_TIME_RANGE,\n",
    "    \"inputsignals\": INPUT_SIGNALS,\n",
    "    \"outputsignalPrefix\": OUTPUT_PREFIX\n",
    "  }\n",
    "}\n",
    "\n",
    "response = requests.post(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows\",\n",
    "    headers=HEADERS,\n",
    "    json=calceval_payload\n",
    ")\n",
    "\n",
    "print (json.dumps(calceval_payload, indent=2))\n",
    "print(\"Status:\", response.status_code)\n",
    "flow_id = response.json().get(\"id\")\n",
    "print(\"CALCEVAL Flow ID:\", flow_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e96a4964",
   "metadata": {},
   "source": [
    "## 7. Create Assessment\n",
    "\n",
    "Send a POST request to `/api/1.3/accounts/{{account_id}}/flows` with `flowType` 'ASSESSMENTSETUP' to create a live assessment for the calculation. Provide the model, assessment name, and input signals."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "852c541b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a live assessment\n",
    "assessment_payload = {\n",
    "    \"description\": \"created from notebook\",\n",
    "    \"flowType\": \"ASSESSMENTSETUP\",\n",
    "    \"name\": \"create assessment\",\n",
    "    \"spec\": {\n",
    "        \"assessmentName\": MODEL_NAME,\n",
    "        \"assessmentState\": \"MONITORING\",\n",
    "        \"inputsignals\": INPUT_SIGNALS,\n",
    "        \"model\": model_id \n",
    "    }\n",
    "}\n",
    "\n",
    "response = requests.post(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows\",\n",
    "    headers=HEADERS,\n",
    "    json=assessment_payload\n",
    ")\n",
    "\n",
    "await sleep (10)\n",
    "print(\"Status:\", response.status_code)\n",
    "print(\"Response:\", response.json())\n",
    "assessment_name = response.json()[\"spec\"][\"assessmentName\"]\n",
    "print(\"Created Assessment:\", assessment_name)\n",
    "\n",
    "response = requests.get(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/assessments\",\n",
    "    headers=HEADERS)\n",
    "\n",
    "count = response.json()[0][\"count\"]\n",
    "\n",
    "response = requests.get(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/assessments?limit={count}\",\n",
    "    headers=HEADERS)\n",
    "\n",
    "response = response.json()\n",
    "for assessment in response:\n",
    "    if assessment[\"name\"] == assessment_name:\n",
    "        assessment_id = assessment[\"id\"]\n",
    "        print(\"Assessment ID:\", assessment_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37960a11",
   "metadata": {},
   "source": [
    "## 8. Get Assessment Info and Extract Output Signals\n",
    "\n",
    "Send a GET request to `/api/1.3/accounts/{{account_id}}/assessments/{{assessment_id}}?denorm=inputSignalset&denorm=outputSignalset` to retrieve assessment info. Extract the `outputsignals` array for use in live calculation evaluation.\n",
    "\n",
    "Wait for the above flow to complete before running the next cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0043fde5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get assessment info and extract output signals\n",
    "response = requests.get(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/assessments/{assessment_id}?denorm=inputSignalset&denorm=outputSignalset\",\n",
    "    headers=HEADERS\n",
    ")\n",
    "\n",
    "links = response.json()[\"links\"]\n",
    "for link in links:\n",
    "    if link[\"name\"] == \"outputSignalset\":\n",
    "        outputsignalset = link[\"object\"][\"signals\"]\n",
    "        outputsignals = [\n",
    "            {\"name\": sig[\"name\"], \"signal\": sig[\"signal\"]}\n",
    "            for sig in outputsignalset\n",
    "        ]\n",
    "\n",
    "print(\"Extracted output signals:\")\n",
    "for sig in outputsignals:\n",
    "    print(sig)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40da46c4",
   "metadata": {},
   "source": [
    "## 9. Run CALCEVAL for Live Assessment Output\n",
    "\n",
    "Send a POST request to `/api/1.3/accounts/{{account_id}}/flows` with `flowType` 'CALCEVAL', providing the extracted `outputsignals` to route rule evaluation results to assessment output signals."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eaeefbe4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Run CALCEVAL for live assessment output\n",
    "calceval_live_payload = {\n",
    "    \"description\": \"Evaluating the Heat Ratio on machine 1\",\n",
    "    \"flowType\": \"CALCEVAL\",\n",
    "    \"name\": \"Live Calc Eval for Heat Ratio\",\n",
    "    \"spec\": {\n",
    "        \"inputsignals\": INPUT_SIGNALS,\n",
    "        \"model\": model_id,\n",
    "        \"outputsignals\": outputsignals,  # Use extracted outputsignals\n",
    "        \"timeRange\": EVAL_TIME_RANGE,\n",
    "        \"workspace\": workspace_id\n",
    "    }\n",
    "}\n",
    "\n",
    "response = requests.post(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows\",\n",
    "    headers=HEADERS,\n",
    "    json=calceval_live_payload\n",
    ")\n",
    "\n",
    "print(\"Status:\", response.status_code)\n",
    "print(\"Response:\", response.json())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5ad27ab",
   "metadata": {},
   "source": [
    "## 10. Stop monitoring an assessment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4fe7f15e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Status: 201\n",
      "Response: {'id': '1514001882975961088', 'type': 'entities.flow', 'tenant': '1356886815915696128', 'flowType': 'STOPMONITORASSESSMENT', 'status': 'CREATED', 'name': 'stop live monitoring for calc assessments', 'spec': {'models': [], 'config': {}, 'query': {'id': ['']}}, 'outputs': [], 'messages': [], 'createTime': 1781036577696, 'updateTime': 1781036577696, 'createdBy': '596582102439518208', 'updatedBy': '596582102439518208', 'archived': False, 'transitions': [{'id': '1', 'status': 'CREATED', 'createTime': '2026-06-09T20:22:57.696000Z'}], 'links': []}\n"
     ]
    }
   ],
   "source": [
    "live_payload = {\n",
    "    \"name\": \"stop live monitoring for calc assessments\",\n",
    "    \"flowType\": \"STOPMONITORASSESSMENT\",\n",
    "    \"spec\":{\n",
    "        \"query\":{\n",
    "            \"id\":[\"\"]   #array assessment IDs to stop live monitoring\n",
    "        }\n",
    "    }\n",
    "\n",
    "}\n",
    "\n",
    "response = requests.post(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows\",\n",
    "    headers=HEADERS,\n",
    "    json=live_payload\n",
    ")\n",
    "\n",
    "print(\"Status:\", response.status_code)\n",
    "print(\"Response:\", response.json())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf74e05b",
   "metadata": {},
   "source": [
    "## 11. Restart monitoring an assessment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "916d1752",
   "metadata": {},
   "outputs": [],
   "source": [
    "live_payload = {\n",
    "    \"name\": \"start live monitoring for calc assessments\",\n",
    "    \"flowType\": \"STARTMONITORASSESSMENT\",\n",
    "    \"spec\":{\n",
    "        \"query\":{\n",
    "            \"id\":[\"\"]   #array assessment IDs to stop live monitoring\n",
    "        }\n",
    "    }\n",
    "\n",
    "}\n",
    "\n",
    "response = requests.post(\n",
    "    f\"{BASE_URL}/accounts/{ACCOUNT_ID}/flows\",\n",
    "    headers=HEADERS,\n",
    "    json=live_payload\n",
    ")\n",
    "\n",
    "print(\"Status:\", response.status_code)\n",
    "print(\"Response:\", response.json())"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv (3.13.7)",
   "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.13.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
