{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "active-ipynb",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from openmdao.utils.notebook_utils import notebook_mode  # noqa: F401\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Generate Tables from Data\n",
    "\n",
    "The `generate_table` function can be used to generate tables in various formats for viewing tabular data.  The formats currently available are:\n",
    "\n",
    "* `text`, a simple text based table\n",
    "* `rst`, a table in restructured text format\n",
    "* `github`, a table in github's markdown format\n",
    "* `grid`, is like the `grid` format in the Tabulate package, which mimics Emacs' table.el package\n",
    "* `simple_grid` draws a grid using single-line box drawing characters\n",
    "* `heavy_grid` draws a grid using thick single-line box drawing characters\n",
    "* `double_grid` draws a grid using double-line box drawing characters\n",
    "* `box_grid` mixes double-line and single-line dashed drawing characters\n",
    "* `html`, a basic html table, viewable in a browser\n",
    "* `tabulator`, a table with sortable, filterable columns that's viewable in a browser, built using the tabulator.js library.\n",
    "\n",
    "All of the `*grid` formats have a corresponding `*outline` format that is identical but\n",
    "doesn't draw lines between data rows.\n",
    "\n",
    "\n",
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.visualization.tables.table_builder.generate_table\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "## Table Row Data\n",
    "\n",
    "Different keyword arguments can be passed to `generate_table` depending on the table format specified via the `tablefmt` argument.  The first argument must always be the data object containing all of the row cell data.  That data object has several allowable formats:\n",
    "\n",
    "\n",
    "1. a 2D numpy array\n",
    "2. an iterator of lists\n",
    "3. a dictionary where each key specifies a column header and each value contains that column's values.\n",
    "4. a list of dictionaries where each dict represents the values of a single row and whose keys are the column headers.\n",
    "\n",
    "\n",
    "Note that for numbers 3 and 4, you must specify `headers='keys'` if you want to use the dictionary keys\n",
    "as the column headers.\n",
    "\n",
    "\n",
    "## Creating Tables\n",
    "\n",
    "There are a core set of arguments accepted for all table formats and some that are table format specific. \n",
    "The arguments that are accepted are those accepted by the `TableBuilder` class that corresponds to each\n",
    "table format.\n",
    "\n",
    "The base class for all tables is `TableBuilder`, and it has the following interface.  Its \n",
    "parameters are accepted by `generate_table` for all table formats.\n",
    "\n",
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.visualization.tables.table_builder.TableBuilder\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "\n",
    "The `headers` argument exists for cases where you just want to specify the header strings without\n",
    "adding any additional metadata for each column.  If you want to do things like set the alignment of\n",
    "a column, you'll need to set the column metadata yourself, either by passing a list of column\n",
    "metadata dicts via the `column_meta` argument, or by calling `update_column_meta` on the table \n",
    "builder object returned from `generate_table`.\n",
    "The `headers` argument also has two special values, `keys` for specifying that the keys\n",
    "of a dictionary should be used as headers, and `firstrow` for specifying that the first row\n",
    "of the given row data will be used as the headers.\n",
    "\n",
    "\n",
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.visualization.tables.table_builder.TableBuilder.update_column_meta\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "### Text Tables\n",
    "\n",
    "The `text`, `rst`, and `github` table formats use table builder classes that all inherit from `TextTableBuilder`.  Its parameters are shown below.\n",
    "\n",
    "\n",
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.visualization.tables.table_builder.TextTableBuilder\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "You can easily create a text based table with custom border strings and column separators by \n",
    "creating a `TextTableBuilder` directly and setting the values of `column_sep`, `top_border`, `bottom_border`, etc.\n",
    "\n",
    "### HTML Tables\n",
    "\n",
    "The `html` table format generates tables in plain HTML table format using `HTMLTableBuilder`. It's parameters are shown below.\n",
    "\n",
    "\n",
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.visualization.tables.table_builder.HTMLTableBuilder\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "The `tabulator` table format generates interactive tables using the `Tabulator.js` library.  These\n",
    "tables can have sortable and/or filterable columns.  They are built by the `TabulatorJSBuilder`\n",
    "class. It's parameters are shown below.\n",
    "\n",
    "\n",
    "```{eval-rst}\n",
    "    .. autofunction:: openmdao.visualization.tables.table_builder.TabulatorJSBuilder\n",
    "       :noindex:\n",
    "```\n",
    "\n",
    "Below are some examples of various table formats and how to create and modify them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openmdao.api as om\n",
    "\n",
    "# Let's create some simple table data\n",
    "rows = [\n",
    "    ['Any bear can dance.', 3.1415926, 42, False],\n",
    "    ['Every frog gets hungry.', -1.33e9, -2, True],\n",
    "    ['Individual jesters keep logs.', 999.99, 1234, False],\n",
    "    ['Many noodles on plates.', 4398.3219, 62835, False],\n",
    "    ['Quaint rosy snakes travel under vases.', .0008654, -7842, False],\n",
    "    ['When Xerxes yawps zestfully.', -1.831e-9, -200, True]\n",
    "]\n",
    "\n",
    "# Now some header strings\n",
    "headers = ['Some Nonsense', 'Some floats', 'Ints', 'Some Bools']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, make a simple text table and display it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = om.generate_table(rows, tablefmt='text', headers=headers)\n",
    "table.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want to limit the width of our table, we can set the `max_width` argument when we create it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = om.generate_table(rows, tablefmt='text', headers=headers, max_width=70)\n",
    "table.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's fix the header alignment of the first column.  Note that column indices start at 0, so we\n",
    "specify 0 for the index of the first column."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table.update_column_meta(0, header_align='center')\n",
    "table.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's try the other table formats.  First, the text based formats."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "formats = ['rst', 'grid', 'simple_grid', 'heavy_grid', 'double_grid', 'box_grid',\n",
    "           'outline', 'simple_outline', 'heavy_outline', 'double_outline', 'box_outline']\n",
    "\n",
    "for fmt in formats:\n",
    "    print(f\"\\n{fmt} table format:\\n\")\n",
    "    table = om.generate_table(rows, tablefmt=fmt, headers=headers)\n",
    "    table.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now for the web based table formats."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = om.generate_table(rows, tablefmt='html', headers=headers)\n",
    "table.display()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = om.generate_table(rows, tablefmt='tabulator', headers=headers)\n",
    "table.display()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the tabulator table above has sortable columns and that the first and last columns are filterable.  Numerical columns are not filterable."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "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.8.13"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
