{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "internal-compensation",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from seeq import spy\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# Set the compatibility option so that you maximize the chance that SPy will remain compatible with your notebook/script\n",
    "spy.options.compatibility = 193"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "usual-insulation",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"background-color: #EEFFEE;color:black; text-align: left;\">Logged in to <strong>http://localhost:34216</strong> as <strong>agent_api_key</strong>.<br>Seeq Server Version: <strong>R66.0.0-SNAPSHOT</strong><br>Seeq SDK Module Version: <strong>66.0.0</strong> @ C:\\git\\crab\\sdk\\pypi\\seeq\\sdk<br>Seeq SPy Module Version: <strong>193.0</strong> @ C:\\git\\crab\\sdk\\pypi\\seeq\\spy</div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Log into Seeq Server if you're not using Seeq Data Lab:\n",
    "spy.login(url='http://localhost:34216', credentials_file='../credentials.key', force=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "coral-house",
   "metadata": {},
   "source": [
    "# Asset Trees 4: Creating Accelerator Templates\n",
    "\n",
    "The Seeq platform provides the basis for doing all sorts of analytics -- leveraging Seeq Workbench, Seeq Organizer, Seeq Data Lab -- with Seeq Cortex providing data connection and calculation capabilities.\n",
    "\n",
    "It can be useful for the Seeq community of partners and users to create _accelerators_ that deploy proven, standardized analytic approaches in rapid fashion using SPy's asset/workbook/topic templating capabilities in `spy.assets`. For example, an expert in monitoring and optimizing large-scale HVAC installations might develop an _Accelerator Template_ to deploy a set of useful calculations, visualizations and reports or dashboards that can handle variations in manufacturer, configuration etc and provide immediate value that can be expanded and built upon over time.\n",
    "\n",
    "This notebook illustrates how to use `spy.assets` infrastructure to create Accelerator Templates. If you haven't used it before, it is recommended that you first explore the following:\n",
    "\n",
    "* [Asset Trees 2 - Templates](Asset%20Trees%202%20-%20Templates.ipynb) to learn about the `spy.assets` infrastructure to create asset trees with calculations\n",
    "* [Asset Trees 3 - Report and Dashboard Templates](Asset%20Trees%203%20-%20Report%20and%20Dashboard%20Templates.ipynb) to learn how to create workbooks and topics (reports/dashboards)\n",
    "\n",
    "This notebook will use the HVAC example data that is installed by default with the Seeq system."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "little-humanitarian",
   "metadata": {},
   "source": [
    "## \"Requirements\"\n",
    "\n",
    "The most important concept with Accelerator Templates is **Requirements**. A Requirement specifies an \"input\" to the template -- something that must be provided in order for the template to function. For example, if the template needs an ambient temperature signal in order to calculate an operational efficiency metric, that temperature signal is a Requirement. Furthermore, the operating limits may need to be provided, or perhaps the manufacturer/model of the equipment. These Requirements are the things that vary from installation to installation. The template can be written to adjust to these variations as long as it has the inputs it needs.\n",
    "\n",
    "Requirements are specified in a class like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "delayed-airport",
   "metadata": {},
   "outputs": [],
   "source": [
    "from seeq.spy.assets import Asset\n",
    "\n",
    "class HVAC_Monitoring_Requirements(Asset):\n",
    "\n",
    "    @Asset.Requirement()\n",
    "    def Compressor_Power(self, metadata):\n",
    "        \"\"\"\n",
    "        Compressor Power as measured at the power supply of the equipment\n",
    "        itself.\n",
    "        \"\"\"\n",
    "        return {\n",
    "            'Type': 'Signal',\n",
    "            'Unit Of Measure Family': 'kW'\n",
    "        }\n",
    "\n",
    "    @Asset.Requirement()\n",
    "    def Temperature(self, metadata):\n",
    "        \"\"\"\n",
    "        The ambient *external* temperature as measured in a neutral location\n",
    "        away from HVAC inlet/outlet sites.\n",
    "\n",
    "        > Note that this value may be derived from equipment sensors with a\n",
    "        > suitable coefficient to account for location bias.\n",
    "        \"\"\"\n",
    "        return {\n",
    "            'Type': 'Signal',\n",
    "            'Unit Of Measure Family': 'F',\n",
    "            'Optional': True\n",
    "        }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "plastic-treasury",
   "metadata": {},
   "source": [
    "Similar to `@Asset.Attribute`, Requirements are specified as functions within a Python class, decorated with `@Asset.Requirement`. The documentation for the function is useful to describe the expectations for the Requirement. For example, the `Temperature` signal that is supplied to meet one of the Requirements above must be an external temperature that is not affected by the operation of the HVAC unit.\n",
    "\n",
    "The return value is a Python dictionary that further constrains the input. In this case, `Temperature` must be a Signal whose unit of measure is compatible with `F` (so, either Fahrenheit, Celsius or Kelvin).\n",
    "\n",
    "`Temperature` is also marked with `'Optional'` equal to `True`. This means that the template can handle the case where an adequate temperature signal cannot be supplied. The template may not be able to provide as many calculations, or perhaps those calculations will be less accurate."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "stone-client",
   "metadata": {},
   "source": [
    "## Analytics!\n",
    "\n",
    "Now let's create a simple class that makes use of these two requirements. In this example, an `Inefficient Operation` condition is calculated by comparing `Compressor Power` and `Temperature` against hard-coded limits. (Such limits should likely be determined by manufacturer specifications or process control limits, but for now we'll keep it simple.)\n",
    "\n",
    "Note how this new `HVAC_Monitoring` class is derived from the `HVAC_Monitoring_Requirements`. This allows the `Inefficient Operation` calculation to refer to the members of the requirements class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "decimal-kitty",
   "metadata": {},
   "outputs": [],
   "source": [
    "class HVAC_Monitoring(HVAC_Monitoring_Requirements):\n",
    "    \n",
    "    @Asset.Attribute()\n",
    "    def Inefficient_Operation(self, metadata):\n",
    "        \"\"\"\n",
    "        A condition that is present whenever the HVAC is running in ostensibly\n",
    "        unnecessary environmental situations, resulting in inefficient use of power.\n",
    "        \n",
    "        Examples:\n",
    "        \n",
    "        * Ambient temperature is within control bounds and does not warrant\n",
    "          cooling\n",
    "        * Environmental conditions are such that HVAC system is operating outside\n",
    "          of its range of capabilities\n",
    "        \"\"\"\n",
    "        \n",
    "        if not self.Temperature():\n",
    "            return None\n",
    "        \n",
    "        return {\n",
    "            'Type': 'Condition',\n",
    "            'Formula Parameters': {\n",
    "                '$power': self.Compressor_Power(),\n",
    "                '$temp': self.Temperature()\n",
    "            },\n",
    "            'Formula': '$power > 20 kW and $temp < 60 F'\n",
    "        }\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bridal-olive",
   "metadata": {},
   "source": [
    "## Input Tree\n",
    "\n",
    "The template is applied to an existing _input tree_. This tree must already be constructed, in whatever way makes sense for a particular use case. It may be different in each application of the template -- different hierarchy, different organization. The template will be applied to the tree as it is constructed, and any roll-ups will correspond to the particular hierarchy present in the input tree.\n",
    "\n",
    "In this example, we will use the **Example** asset tree that comes with Seeq. So we assemble a _metadata DataFrame_ by doing `spy.search(recursive=True)`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "powerful-campus",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"background-color: #FFFFCC; color:black;\">This search result includes Assets. Consider passing in \"old_asset_format=False\" so that the \"Path\" and \"Asset\" columns are populated in a way that is consistent with all other aspects of SPy. The default behavior without this argument emulates the (incorrect) way SPy would return such results historically.</div><div style=\"background-color: #EEFFEE;color:black; text-align: left;\">Query successful</div><table class=\"tex2jax_ignore\" style=\"color:black;\"><tr><td style=\"background-color: #EEFFEE;\"></td><td style=\"background-color: #EEFFEE; text-align: left;\">Path</td><td style=\"background-color: #EEFFEE; text-align: right;\">Time</td><td style=\"background-color: #EEFFEE; text-align: right;\">Count</td><td style=\"background-color: #EEFFEE; text-align: right;\">Pages</td><td style=\"background-color: #EEFFEE; text-align: left;\">Result</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">0</td><td style=\"text-align: left; vertical-align: top;\">Example</td><td style=\"vertical-align: top;\">00:00:00.21</td><td style=\"text-align: right; vertical-align: top;\">112</td><td style=\"text-align: right; vertical-align: top;\">1</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>ID</th>\n",
       "      <th>Path</th>\n",
       "      <th>Asset</th>\n",
       "      <th>Name</th>\n",
       "      <th>Type</th>\n",
       "      <th>Build Path</th>\n",
       "      <th>Build Asset</th>\n",
       "      <th>Build Template</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0EF58FC2-7F03-6030-8D14-ACDB6656F424</td>\n",
       "      <td>Example &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area J</td>\n",
       "      <td>Optimizer</td>\n",
       "      <td>StoredSignal</td>\n",
       "      <td>HVAC Monitoring &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area J</td>\n",
       "      <td>HVAC_Monitoring</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0EF58FC2-7E13-EC10-9477-084EDE9A9A43</td>\n",
       "      <td>Example &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area G</td>\n",
       "      <td>Wet Bulb</td>\n",
       "      <td>StoredSignal</td>\n",
       "      <td>HVAC Monitoring &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area G</td>\n",
       "      <td>HVAC_Monitoring</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0EF58FC2-7E44-F950-A787-28E3E863826D</td>\n",
       "      <td>Example &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area C</td>\n",
       "      <td>Compressor Stage</td>\n",
       "      <td>StoredSignal</td>\n",
       "      <td>HVAC Monitoring &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area C</td>\n",
       "      <td>HVAC_Monitoring</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0EF58FC2-7E6E-7160-ACE8-E1D5408B23E1</td>\n",
       "      <td>Example &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area A</td>\n",
       "      <td>Relative Humidity</td>\n",
       "      <td>StoredSignal</td>\n",
       "      <td>HVAC Monitoring &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area A</td>\n",
       "      <td>HVAC_Monitoring</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0EF58FC2-7E7F-62D0-A7D2-ABD72CAE3AC6</td>\n",
       "      <td>Example &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area I</td>\n",
       "      <td>Temperature</td>\n",
       "      <td>StoredSignal</td>\n",
       "      <td>HVAC Monitoring &gt;&gt; Cooling Tower 1</td>\n",
       "      <td>Area I</td>\n",
       "      <td>HVAC_Monitoring</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                     ID                        Path   Asset  \\\n",
       "0  0EF58FC2-7F03-6030-8D14-ACDB6656F424  Example >> Cooling Tower 1  Area J   \n",
       "1  0EF58FC2-7E13-EC10-9477-084EDE9A9A43  Example >> Cooling Tower 1  Area G   \n",
       "2  0EF58FC2-7E44-F950-A787-28E3E863826D  Example >> Cooling Tower 1  Area C   \n",
       "3  0EF58FC2-7E6E-7160-ACE8-E1D5408B23E1  Example >> Cooling Tower 1  Area A   \n",
       "4  0EF58FC2-7E7F-62D0-A7D2-ABD72CAE3AC6  Example >> Cooling Tower 1  Area I   \n",
       "\n",
       "                Name          Type                          Build Path  \\\n",
       "0          Optimizer  StoredSignal  HVAC Monitoring >> Cooling Tower 1   \n",
       "1           Wet Bulb  StoredSignal  HVAC Monitoring >> Cooling Tower 1   \n",
       "2   Compressor Stage  StoredSignal  HVAC Monitoring >> Cooling Tower 1   \n",
       "3  Relative Humidity  StoredSignal  HVAC Monitoring >> Cooling Tower 1   \n",
       "4        Temperature  StoredSignal  HVAC Monitoring >> Cooling Tower 1   \n",
       "\n",
       "  Build Asset   Build Template  \n",
       "0      Area J  HVAC_Monitoring  \n",
       "1      Area G  HVAC_Monitoring  \n",
       "2      Area C  HVAC_Monitoring  \n",
       "3      Area A  HVAC_Monitoring  \n",
       "4      Area I  HVAC_Monitoring  "
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Grab the whole tree as the basis of the metadata DataFrame\n",
    "metadata_df = spy.search({\n",
    "    'Path': 'Example'\n",
    "}, recursive=True)\n",
    "\n",
    "# Reduce the number of columns (just for readability)\n",
    "metadata_df = metadata_df[['ID', 'Path', 'Asset', 'Name', 'Type']]\n",
    "\n",
    "# This call prepares the metadata DataFrame by adding 'Build Path' and\n",
    "# 'Build Asset' columns. You can also specify a new root_asset_name, which\n",
    "# is often desired so that you can differentiate the output tree from the\n",
    "# input tree.\n",
    "spy.assets.prepare(metadata_df, root_asset_name='HVAC Monitoring')\n",
    "\n",
    "# Specify the Build Template. We want to target all leaf \"Area\" assets (for now)\n",
    "metadata_df.at[metadata_df['Build Asset'].str.startswith('Area '), 'Build Template'] = 'HVAC_Monitoring'\n",
    "\n",
    "metadata_df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ecological-parts",
   "metadata": {},
   "source": [
    "## Build and Push\n",
    "\n",
    "As with other uses of `spy.assets`, we must now build and then push the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "verified-cement",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"background-color: #EEFFEE;color:black; text-align: left;\">Successfully built 11 assets and 31 attributes.</div><table class=\"tex2jax_ignore\" style=\"color:black;\"><tr><td style=\"background-color: #EEFFEE;\"></td><td style=\"background-color: #EEFFEE; text-align: left;\">Build Path</td><td style=\"background-color: #EEFFEE; text-align: left;\">Build Asset</td><td style=\"background-color: #EEFFEE; text-align: left;\">Build Template</td><td style=\"background-color: #EEFFEE; text-align: left;\">Build Result</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">0</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area J</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">1</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area G</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">2</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area C</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">3</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area A</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">4</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area I</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">5</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area B</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">6</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area K</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">7</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 1</td><td style=\"text-align: left; vertical-align: top;\">Area H</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">8</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 2</td><td style=\"text-align: left; vertical-align: top;\">Area E</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">9</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 2</td><td style=\"text-align: left; vertical-align: top;\">Area F</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr><tr style=\"background-color: #EEFFEE;\"><td style=\"vertical-align: top;\">10</td><td style=\"text-align: left; vertical-align: top;\">HVAC Monitoring >> Cooling Tower 2</td><td style=\"text-align: left; vertical-align: top;\">Area D</td><td style=\"text-align: left; vertical-align: top;\">HVAC_Monitoring</td><td style=\"text-align: left; vertical-align: top;\">Success</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "build_df = spy.assets.build(HVAC_Monitoring, metadata_df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "colonial-orange",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"background-color: #EEFFEE;color:black; text-align: left;\">Pushed successfully to datasource <strong>Seeq Data Lab [Datasource ID: Seeq Data Lab]</strong> and scoped to workbook ID <strong>0EF5BE30-373A-E8B0-A946-CF5B78245CC1</strong><br>Click the following link to see what you pushed in Seeq:<br><a href=\"http://localhost:34216/0EF5BE30-36B4-6440-9F84-F7300BD1965D/workbook/0EF5BE30-373A-E8B0-A946-CF5B78245CC1/worksheet/0EF5BE30-37A5-FF70-9E2A-7AEA03ADEC7D\" target=\"_blank\">http://localhost:34216/0EF5BE30-36B4-6440-9F84-F7300BD1965D/workbook/0EF5BE30-373A-E8B0-A946-CF5B78245CC1/worksheet/0EF5BE30-37A5-FF70-9E2A-7AEA03ADEC7D</a></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "push_results_df = spy.push(metadata=build_df)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "operational-number",
   "metadata": {},
   "source": [
    "If you click on the workbook link, you should see a new `HVAC Monitoring` tree in the **Data** tab with the expected hierarchy and an `Inefficient Operation` condition under each Area."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "satellite-gossip",
   "metadata": {},
   "source": [
    "## Brochures\n",
    "\n",
    "Once you have an accelerator created, you can generate a _brochure_ that advertises the cool analytics that the accelerator provides.\n",
    "\n",
    "Execute the `spy.assets.brochure()` function like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "lovely-session",
   "metadata": {},
   "outputs": [],
   "source": [
    "html = spy.assets.brochure(HVAC_Monitoring, output='html')\n",
    "\n",
    "# Write it to a file so we can view it in the browser\n",
    "os.makedirs('Output', exist_ok=True)\n",
    "with open('Output/hvac_monitoring_brochure.html', 'w') as f:\n",
    "    f.write(html)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "respected-bearing",
   "metadata": {},
   "source": [
    "Now take a look at the HTML by clicking here:\n",
    "\n",
    "[Output/hvac_monitoring_brochure.html](Output/hvac_monitoring_brochure.html)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.11",
   "language": "python",
   "name": "python311"
  },
  "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.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
