s2t2/gspread-models-py

View on GitHub
docs/notebooks/demo_v1_0_6.ipynb

Summary

Maintainability
Test Coverage
{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "k5Ny8fDoMqNE"
      },
      "source": [
        "# Demo Notebook"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aairNG8GMqNG"
      },
      "source": [
        "<a target=\"_blank\" href=\"https://colab.research.google.com/github/s2t2/gspread-models-py/blob/main/docs/notebooks/demo_v1_0_6.ipynb\">\n",
        "  <img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/>\n",
        "</a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xouDdMjFMqNH"
      },
      "source": [
        "This is a Google Colab notebook that showcases various features of the [`gspread-models` package](https://github.com/s2t2/gspread-models-py).\n",
        "\n",
        "In this demo, we take advantage of the simplified Google authentication process within Colab to obtain credentials for the logged in user (instead of using a service account credentials JSON file)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dVY-MEQMfBKx"
      },
      "source": [
        "## Package Installation"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iRNmtlPzg4-i"
      },
      "source": [
        "Package installation:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 59,
      "metadata": {
        "id": "4AHQ9Raue_bn"
      },
      "outputs": [],
      "source": [
        "%%capture\n",
        "#!pip uninstall gspread_models\n",
        "!pip install git+https://github.com/s2t2/gspread-models-py.git@binding\n",
        "#!pip install gspread_models==1.0.6"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 60,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "sAo-H0FafPVy",
        "outputId": "6ede0583-bb4e-4597-e32d-3ba2dec3d2f4"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "gspread-models                   1.0.6\n"
          ]
        }
      ],
      "source": [
        "#!pip list | grep gspread_models\n",
        "!pip list | grep gspread-models"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "from gspread_models import VERSION\n",
        "print(VERSION)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MAj_N4n513gR",
        "outputId": "1d544d52-86ea-40c0-a23d-c65c7e6059f5"
      },
      "execution_count": 61,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "1.0.6\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e1i24s_Ugx4o"
      },
      "source": [
        "## Google Credentials"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Asks you to login via google account:"
      ],
      "metadata": {
        "id": "0pFarxnbJskv"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 62,
      "metadata": {
        "id": "HlOn7AgKFh1R"
      },
      "outputs": [],
      "source": [
        "from google.colab import auth\n",
        "\n",
        "auth.authenticate_user()"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Gets credentials for the logged in user:"
      ],
      "metadata": {
        "id": "zM0SZ0moJ38d"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 63,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 204
        },
        "id": "Ib7gP85SG3CG",
        "outputId": "39367a86-fa1f-42c0-b750-24abbf266bf6"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "google.auth.compute_engine.credentials.Credentials"
            ],
            "text/html": [
              "<div style=\"max-width:800px; border: 1px solid var(--colab-border-color);\"><style>\n",
              "      pre.function-repr-contents {\n",
              "        overflow-x: auto;\n",
              "        padding: 8px 12px;\n",
              "        max-height: 500px;\n",
              "      }\n",
              "\n",
              "      pre.function-repr-contents.function-repr-contents-collapsed {\n",
              "        cursor: pointer;\n",
              "        max-height: 100px;\n",
              "      }\n",
              "    </style>\n",
              "    <pre style=\"white-space: initial; background:\n",
              "         var(--colab-secondary-surface-color); padding: 8px 12px;\n",
              "         border-bottom: 1px solid var(--colab-border-color);\"><b>google.auth.compute_engine.credentials.Credentials</b><br/>def __init__(service_account_email=&#x27;default&#x27;, quota_project_id=None, scopes=None, default_scopes=None, universe_domain=None)</pre><pre class=\"function-repr-contents function-repr-contents-collapsed\" style=\"\"><a class=\"filepath\" style=\"display:none\" href=\"#\">/usr/local/lib/python3.10/dist-packages/google/auth/compute_engine/credentials.py</a>Compute Engine Credentials.\n",
              "\n",
              "These credentials use the Google Compute Engine metadata server to obtain\n",
              "OAuth 2.0 access tokens associated with the instance&#x27;s service account,\n",
              "and are also used for Cloud Run, Flex and App Engine (except for the Python\n",
              "2.7 runtime, which is supported only on older versions of this library).\n",
              "\n",
              "For more information about Compute Engine authentication, including how\n",
              "to configure scopes, see the `Compute Engine authentication\n",
              "documentation`_.\n",
              "\n",
              ".. note:: On Compute Engine the metadata server ignores requested scopes.\n",
              "    On Cloud Run, Flex and App Engine the server honours requested scopes.\n",
              "\n",
              ".. _Compute Engine authentication documentation:\n",
              "    https://cloud.google.com/compute/docs/authentication#using</pre>\n",
              "      <script>\n",
              "      if (google.colab.kernel.accessAllowed && google.colab.files && google.colab.files.view) {\n",
              "        for (const element of document.querySelectorAll('.filepath')) {\n",
              "          element.style.display = 'block'\n",
              "          element.onclick = (event) => {\n",
              "            event.preventDefault();\n",
              "            event.stopPropagation();\n",
              "            google.colab.files.view(element.textContent, 35);\n",
              "          };\n",
              "        }\n",
              "      }\n",
              "      for (const element of document.querySelectorAll('.function-repr-contents')) {\n",
              "        element.onclick = (event) => {\n",
              "          event.preventDefault();\n",
              "          event.stopPropagation();\n",
              "          element.classList.toggle('function-repr-contents-collapsed');\n",
              "        };\n",
              "      }\n",
              "      </script>\n",
              "      </div>"
            ]
          },
          "metadata": {},
          "execution_count": 63
        }
      ],
      "source": [
        "from google.auth import default\n",
        "\n",
        "creds, _ = default()\n",
        "type(creds)"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Google Sheets Setup"
      ],
      "metadata": {
        "id": "TXhcXdUrRAKj"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Create a Google Sheets document with \"books\" sheet, with columns: `id`, `title`, `author`, `year`, and `created_at`.\n",
        "\n",
        "Note the document identifier, and set it as the `GSPREAD_MODELS_DOCUMENT_ID` notebook secret."
      ],
      "metadata": {
        "id": "G3HcQfRQRKCJ"
      }
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OGjaDr9Vez0e"
      },
      "source": [
        "Use the document identifier from notebook secrets:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 64,
      "metadata": {
        "id": "J4eXoNQHgawN"
      },
      "outputs": [],
      "source": [
        "from google.colab import userdata\n",
        "\n",
        "GOOGLE_SHEETS_DOCUMENT_ID = userdata.get(\"GSPREAD_MODELS_DOCUMENT_ID\") or \"your-document-id-here\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OG5YBx0CdSYx"
      },
      "source": [
        "## Spreadsheet Service"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "The spreadsheet service provides a lower level interface into the configured google sheet document. You can use it if you like."
      ],
      "metadata": {
        "id": "ohOcIBbXR3xd"
      }
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pSQ8SNOkj6h9"
      },
      "source": [
        "Build a new service instance:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 65,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6mqMJycrgpZC",
        "outputId": "2ea90c1f-348c-4f63-adf5-604b10e915e8"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "SPREADSHEET SERVICE...\n",
            "DOCUMENT ID: 1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs\n"
          ]
        }
      ],
      "source": [
        "from gspread_models.service import SpreadsheetService\n",
        "\n",
        "service = SpreadsheetService(creds=creds, document_id=GOOGLE_SHEETS_DOCUMENT_ID)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 66,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MsySNzYWkBak",
        "outputId": "9ae57b9c-d53b-4ab1-d6a1-24394332c392"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<Spreadsheet 'GSpread Models (Test Database)' id:1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs>"
            ]
          },
          "metadata": {},
          "execution_count": 66
        }
      ],
      "source": [
        "service.doc"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 67,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "FXBBG3dwj95e",
        "outputId": "7707b2d1-7796-455c-b431-a1c542668a9c"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Info\n",
            "products\n",
            "orders\n",
            "books\n"
          ]
        }
      ],
      "source": [
        "for sheet in service.sheets:\n",
        "    print(sheet.title)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zGcO45SgdWy6"
      },
      "source": [
        "## Binding the Base Model"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wM3DWDLwjJz2"
      },
      "source": [
        "Bind the base model with access to your account credentials and your document:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 68,
      "metadata": {
        "id": "TZNcgKtjgIvH",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "6226ecf6-40de-4b1f-f407-b3e15d1d88a2"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "SPREADSHEET SERVICE...\n",
            "DOCUMENT ID: 1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs\n"
          ]
        }
      ],
      "source": [
        "from gspread_models.base import BaseModel\n",
        "\n",
        "# if you already have the service handy, you can set it:\n",
        "#BaseModel.service = service\n",
        "\n",
        "# otherwise you can bypass the service altogether:\n",
        "BaseModel.bind(credentials=creds, document_id=GOOGLE_SHEETS_DOCUMENT_ID)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZAMpwfwZdb-U"
      },
      "source": [
        "## Defining Child Model Classes"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1094P6NBjTNq"
      },
      "source": [
        "Now, defining custom classes that inherit from the base model:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 69,
      "metadata": {
        "id": "GYTgcrjMfIrA"
      },
      "outputs": [],
      "source": [
        "class Book(BaseModel):\n",
        "\n",
        "    SHEET_NAME = \"books\"\n",
        "\n",
        "    COLUMNS = [\"title\", \"author\", \"year\"]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9L_sHhcsdfzX"
      },
      "source": [
        "## Queries"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hAGNABrldiRb"
      },
      "source": [
        "Destroy all records:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 70,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "o3mSWe0Wroy8",
        "outputId": "d84c4c27-3019-4f15-ba73-8796bc051674"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "GET SHEET ('books')...\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'spreadsheetId': '1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs',\n",
              " 'replies': [{}]}"
            ]
          },
          "metadata": {},
          "execution_count": 70
        }
      ],
      "source": [
        "Book.destroy_all()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kYRFnqBGdkXA"
      },
      "source": [
        "Populate sheet with default records:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 71,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "meLRpTPtcubm",
        "outputId": "22ff9429-aaa8-4b70-b704-a6f256b089ea"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'spreadsheetId': '1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs',\n",
              " 'tableRange': 'books!A1:F1',\n",
              " 'updates': {'spreadsheetId': '1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs',\n",
              "  'updatedRange': 'books!A2:E13',\n",
              "  'updatedRows': 12,\n",
              "  'updatedColumns': 5,\n",
              "  'updatedCells': 60}}"
            ]
          },
          "metadata": {},
          "execution_count": 71
        }
      ],
      "source": [
        "Book.create_all([\n",
        "    {\"title\": \"To Kill a Mockingbird\", \"author\": \"Harper Lee\", \"year\": 1960},\n",
        "    {\"title\": \"1984\", \"author\": \"George Orwell\", \"year\": 1949},\n",
        "    {\"title\": \"The Great Gatsby\", \"author\": \"F. Scott Fitzgerald\", \"year\": 1925},\n",
        "    {\"title\": \"The Catcher in the Rye\", \"author\": \"J.D. Salinger\", \"year\": 1951},\n",
        "    {\"title\": \"Pride and Prejudice\", \"author\": \"Jane Austen\", \"year\": 1813},\n",
        "    {\"title\": \"To the Lighthouse\", \"author\": \"Virginia Woolf\", \"year\": 1927},\n",
        "    {\"title\": \"The Hobbit\", \"author\": \"J.R.R. Tolkien\", \"year\": 1937},\n",
        "    {\"title\": \"Moby-Dick\", \"author\": \"Herman Melville\", \"year\": 1851},\n",
        "    {\"title\": \"Brave New World\", \"author\": \"Aldous Huxley\", \"year\": 1932},\n",
        "    {\"title\": \"Alice's Adventures in Wonderland\", \"author\": \"Lewis Carroll\", \"year\": 1865},\n",
        "    {\"title\": \"Harry Potter and the Philosopher's Stone\", \"author\": \"J.K. Rowling\", \"year\": 1997},\n",
        "    {\"title\": \"Harry Potter and the Chamber of Secrets\", \"author\": \"J.K. Rowling\", \"year\": 1998},\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "w3fb_9_Xdmhi"
      },
      "source": [
        "Fetch all records from the sheet:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 72,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0ilfQfXkcwmn",
        "outputId": "0ab3ac27-dc84-4090-f2f5-4df9db8be941"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "12"
            ]
          },
          "metadata": {},
          "execution_count": 72
        }
      ],
      "source": [
        "books = Book.all()\n",
        "len(books)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 73,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "q0mnDu7Ic2-Q",
        "outputId": "4b659476-117c-4e14-dcc8-d0d71b29a3b6"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "1 | To Kill a Mockingbird | Harper Lee\n",
            "2 | 1984 | George Orwell\n",
            "3 | The Great Gatsby | F. Scott Fitzgerald\n",
            "4 | The Catcher in the Rye | J.D. Salinger\n",
            "5 | Pride and Prejudice | Jane Austen\n",
            "6 | To the Lighthouse | Virginia Woolf\n",
            "7 | The Hobbit | J.R.R. Tolkien\n",
            "8 | Moby-Dick | Herman Melville\n",
            "9 | Brave New World | Aldous Huxley\n",
            "10 | Alice's Adventures in Wonderland | Lewis Carroll\n",
            "11 | Harry Potter and the Philosopher's Stone | J.K. Rowling\n",
            "12 | Harry Potter and the Chamber of Secrets | J.K. Rowling\n"
          ]
        }
      ],
      "source": [
        "for book in books:\n",
        "    print(book.id, book.title, book.author, sep=\" | \")\n",
        "    # book.created_at.strftime(\"%Y-%m-%d\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "beMrbSlsdvAI"
      },
      "source": [
        "Find record by id:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 74,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ynt7cK9-drjw",
        "outputId": "b8cc3240-034e-41db-e9ff-e90404f99c60"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'id': 3,\n",
              " 'title': 'The Great Gatsby',\n",
              " 'author': 'F. Scott Fitzgerald',\n",
              " 'year': 1925,\n",
              " 'created_at': datetime.datetime(2024, 5, 23, 2, 30, 33, 561181, tzinfo=datetime.timezone.utc)}"
            ]
          },
          "metadata": {},
          "execution_count": 74
        }
      ],
      "source": [
        "book = Book.find(3)\n",
        "dict(book)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zXbOGHfGdo3_"
      },
      "source": [
        "Filter records on matching conditions:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 75,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Ym0-l6_vd8yi",
        "outputId": "34f3df29-f62f-461a-e32d-fc764b07e789"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'id': 3,\n",
              " 'title': 'The Great Gatsby',\n",
              " 'author': 'F. Scott Fitzgerald',\n",
              " 'year': 1925,\n",
              " 'created_at': datetime.datetime(2024, 5, 23, 2, 30, 33, 561181, tzinfo=datetime.timezone.utc)}"
            ]
          },
          "metadata": {},
          "execution_count": 75
        }
      ],
      "source": [
        "book = Book.where(author=\"F. Scott Fitzgerald\")[0]\n",
        "dict(book)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 76,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "2KsqY75aeIJ-",
        "outputId": "a32106c9-f0f0-47fb-b28e-ac3ae9bcc721"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "11 | Harry Potter and the Philosopher's Stone | J.K. Rowling\n",
            "12 | Harry Potter and the Chamber of Secrets | J.K. Rowling\n"
          ]
        }
      ],
      "source": [
        "hp_books = Book.where(author=\"J.K. Rowling\")\n",
        "for book in hp_books:\n",
        "    print(book.id, book.title, book.author, sep=\" | \")"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Pandas Support"
      ],
      "metadata": {
        "id": "kqMsNINQeFJG"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Formatting Records as Pandas DataFrame\n",
        "\n",
        "FYI: It is easy to construct a pandas DataFrame containing the information fetched from the sheet, by converting the records to a list of dictionaries:"
      ],
      "metadata": {
        "id": "y-5bLjaLPoP9"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from pandas import DataFrame\n",
        "\n",
        "books_df = DataFrame([dict(book) for book in books])\n",
        "books_df.head()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 206
        },
        "id": "JD7XitraPPsi",
        "outputId": "dd923317-9181-4d26-f839-d47c6aa85329"
      },
      "execution_count": 77,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "   id                   title               author  year  \\\n",
              "0   1   To Kill a Mockingbird           Harper Lee  1960   \n",
              "1   2                    1984        George Orwell  1949   \n",
              "2   3        The Great Gatsby  F. Scott Fitzgerald  1925   \n",
              "3   4  The Catcher in the Rye        J.D. Salinger  1951   \n",
              "4   5     Pride and Prejudice          Jane Austen  1813   \n",
              "\n",
              "                        created_at  \n",
              "0 2024-05-23 02:30:33.559636+00:00  \n",
              "1 2024-05-23 02:30:33.561128+00:00  \n",
              "2 2024-05-23 02:30:33.561181+00:00  \n",
              "3 2024-05-23 02:30:33.561222+00:00  \n",
              "4 2024-05-23 02:30:33.561261+00:00  "
            ],
            "text/html": [
              "\n",
              "  <div id=\"df-47b068ad-a555-4fe2-b8d7-bf00cf27adb8\" class=\"colab-df-container\">\n",
              "    <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>title</th>\n",
              "      <th>author</th>\n",
              "      <th>year</th>\n",
              "      <th>created_at</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>1</td>\n",
              "      <td>To Kill a Mockingbird</td>\n",
              "      <td>Harper Lee</td>\n",
              "      <td>1960</td>\n",
              "      <td>2024-05-23 02:30:33.559636+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>2</td>\n",
              "      <td>1984</td>\n",
              "      <td>George Orwell</td>\n",
              "      <td>1949</td>\n",
              "      <td>2024-05-23 02:30:33.561128+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>3</td>\n",
              "      <td>The Great Gatsby</td>\n",
              "      <td>F. Scott Fitzgerald</td>\n",
              "      <td>1925</td>\n",
              "      <td>2024-05-23 02:30:33.561181+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>4</td>\n",
              "      <td>The Catcher in the Rye</td>\n",
              "      <td>J.D. Salinger</td>\n",
              "      <td>1951</td>\n",
              "      <td>2024-05-23 02:30:33.561222+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>5</td>\n",
              "      <td>Pride and Prejudice</td>\n",
              "      <td>Jane Austen</td>\n",
              "      <td>1813</td>\n",
              "      <td>2024-05-23 02:30:33.561261+00:00</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>\n",
              "    <div class=\"colab-df-buttons\">\n",
              "\n",
              "  <div class=\"colab-df-container\">\n",
              "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-47b068ad-a555-4fe2-b8d7-bf00cf27adb8')\"\n",
              "            title=\"Convert this dataframe to an interactive table.\"\n",
              "            style=\"display:none;\">\n",
              "\n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
              "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
              "  </svg>\n",
              "    </button>\n",
              "\n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    .colab-df-buttons div {\n",
              "      margin-bottom: 4px;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "    <script>\n",
              "      const buttonEl =\n",
              "        document.querySelector('#df-47b068ad-a555-4fe2-b8d7-bf00cf27adb8 button.colab-df-convert');\n",
              "      buttonEl.style.display =\n",
              "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "      async function convertToInteractive(key) {\n",
              "        const element = document.querySelector('#df-47b068ad-a555-4fe2-b8d7-bf00cf27adb8');\n",
              "        const dataTable =\n",
              "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                    [key], {});\n",
              "        if (!dataTable) return;\n",
              "\n",
              "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "          + ' to learn more about interactive tables.';\n",
              "        element.innerHTML = '';\n",
              "        dataTable['output_type'] = 'display_data';\n",
              "        await google.colab.output.renderOutput(dataTable, element);\n",
              "        const docLink = document.createElement('div');\n",
              "        docLink.innerHTML = docLinkHtml;\n",
              "        element.appendChild(docLink);\n",
              "      }\n",
              "    </script>\n",
              "  </div>\n",
              "\n",
              "\n",
              "<div id=\"df-f7974d79-b3ea-4f3a-ab8a-8779fa70216d\">\n",
              "  <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-f7974d79-b3ea-4f3a-ab8a-8779fa70216d')\"\n",
              "            title=\"Suggest charts\"\n",
              "            style=\"display:none;\">\n",
              "\n",
              "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "     width=\"24px\">\n",
              "    <g>\n",
              "        <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
              "    </g>\n",
              "</svg>\n",
              "  </button>\n",
              "\n",
              "<style>\n",
              "  .colab-df-quickchart {\n",
              "      --bg-color: #E8F0FE;\n",
              "      --fill-color: #1967D2;\n",
              "      --hover-bg-color: #E2EBFA;\n",
              "      --hover-fill-color: #174EA6;\n",
              "      --disabled-fill-color: #AAA;\n",
              "      --disabled-bg-color: #DDD;\n",
              "  }\n",
              "\n",
              "  [theme=dark] .colab-df-quickchart {\n",
              "      --bg-color: #3B4455;\n",
              "      --fill-color: #D2E3FC;\n",
              "      --hover-bg-color: #434B5C;\n",
              "      --hover-fill-color: #FFFFFF;\n",
              "      --disabled-bg-color: #3B4455;\n",
              "      --disabled-fill-color: #666;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart {\n",
              "    background-color: var(--bg-color);\n",
              "    border: none;\n",
              "    border-radius: 50%;\n",
              "    cursor: pointer;\n",
              "    display: none;\n",
              "    fill: var(--fill-color);\n",
              "    height: 32px;\n",
              "    padding: 0;\n",
              "    width: 32px;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart:hover {\n",
              "    background-color: var(--hover-bg-color);\n",
              "    box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "    fill: var(--button-hover-fill-color);\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart-complete:disabled,\n",
              "  .colab-df-quickchart-complete:disabled:hover {\n",
              "    background-color: var(--disabled-bg-color);\n",
              "    fill: var(--disabled-fill-color);\n",
              "    box-shadow: none;\n",
              "  }\n",
              "\n",
              "  .colab-df-spinner {\n",
              "    border: 2px solid var(--fill-color);\n",
              "    border-color: transparent;\n",
              "    border-bottom-color: var(--fill-color);\n",
              "    animation:\n",
              "      spin 1s steps(1) infinite;\n",
              "  }\n",
              "\n",
              "  @keyframes spin {\n",
              "    0% {\n",
              "      border-color: transparent;\n",
              "      border-bottom-color: var(--fill-color);\n",
              "      border-left-color: var(--fill-color);\n",
              "    }\n",
              "    20% {\n",
              "      border-color: transparent;\n",
              "      border-left-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "    }\n",
              "    30% {\n",
              "      border-color: transparent;\n",
              "      border-left-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "      border-right-color: var(--fill-color);\n",
              "    }\n",
              "    40% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "    }\n",
              "    60% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "    }\n",
              "    80% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "      border-bottom-color: var(--fill-color);\n",
              "    }\n",
              "    90% {\n",
              "      border-color: transparent;\n",
              "      border-bottom-color: var(--fill-color);\n",
              "    }\n",
              "  }\n",
              "</style>\n",
              "\n",
              "  <script>\n",
              "    async function quickchart(key) {\n",
              "      const quickchartButtonEl =\n",
              "        document.querySelector('#' + key + ' button');\n",
              "      quickchartButtonEl.disabled = true;  // To prevent multiple clicks.\n",
              "      quickchartButtonEl.classList.add('colab-df-spinner');\n",
              "      try {\n",
              "        const charts = await google.colab.kernel.invokeFunction(\n",
              "            'suggestCharts', [key], {});\n",
              "      } catch (error) {\n",
              "        console.error('Error during call to suggestCharts:', error);\n",
              "      }\n",
              "      quickchartButtonEl.classList.remove('colab-df-spinner');\n",
              "      quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
              "    }\n",
              "    (() => {\n",
              "      let quickchartButtonEl =\n",
              "        document.querySelector('#df-f7974d79-b3ea-4f3a-ab8a-8779fa70216d button');\n",
              "      quickchartButtonEl.style.display =\n",
              "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "    })();\n",
              "  </script>\n",
              "</div>\n",
              "\n",
              "    </div>\n",
              "  </div>\n"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "dataframe",
              "variable_name": "books_df",
              "summary": "{\n  \"name\": \"books_df\",\n  \"rows\": 12,\n  \"fields\": [\n    {\n      \"column\": \"id\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 3,\n        \"min\": 1,\n        \"max\": 12,\n        \"num_unique_values\": 12,\n        \"samples\": [\n          11,\n          10,\n          1\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"title\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 12,\n        \"samples\": [\n          \"Harry Potter and the Philosopher's Stone\",\n          \"Alice's Adventures in Wonderland\",\n          \"To Kill a Mockingbird\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"author\",\n      \"properties\": {\n        \"dtype\": \"string\",\n        \"num_unique_values\": 11,\n        \"samples\": [\n          \"Virginia Woolf\",\n          \"Harper Lee\",\n          \"Lewis Carroll\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"year\",\n      \"properties\": {\n        \"dtype\": \"number\",\n        \"std\": 56,\n        \"min\": 1813,\n        \"max\": 1998,\n        \"num_unique_values\": 12,\n        \"samples\": [\n          1997,\n          1865,\n          1960\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    },\n    {\n      \"column\": \"created_at\",\n      \"properties\": {\n        \"dtype\": \"date\",\n        \"min\": \"2024-05-23 02:30:33.559636+00:00\",\n        \"max\": \"2024-05-23 02:30:33.561513+00:00\",\n        \"num_unique_values\": 12,\n        \"samples\": [\n          \"2024-05-23 02:30:33.561476+00:00\",\n          \"2024-05-23 02:30:33.561439+00:00\",\n          \"2024-05-23 02:30:33.559636+00:00\"\n        ],\n        \"semantic_type\": \"\",\n        \"description\": \"\"\n      }\n    }\n  ]\n}"
            }
          },
          "metadata": {},
          "execution_count": 77
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Customizing Base Model Behavior"
      ],
      "metadata": {
        "id": "BuT7G929YGI7"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "If you would like to build additional pandas functionality into your child classes, you can leverage inheritence to overwrite methods of the base model:"
      ],
      "metadata": {
        "id": "KHjeEFPkTrLA"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from pandas import DataFrame\n",
        "\n",
        "class MyBaseModel(BaseModel):\n",
        "\n",
        "    @classmethod\n",
        "    def all(cls, as_df=False):\n",
        "        \"\"\"Fetches all records from a given sheet.\n",
        "\n",
        "            Overwrites parent method to optionally use a dataframe approach.\n",
        "\n",
        "            Params : as_df (bool) : will return data as a DataFrame\n",
        "        \"\"\"\n",
        "        records = cls.sheet.get_all_records()\n",
        "        if as_df:\n",
        "            # use model conversion to take advantage of datetime-conversion, etc:\n",
        "            df = DataFrame([dict(cls(record)) for record in records])\n",
        "            df.index = df[\"id\"]\n",
        "            return df\n",
        "        else:\n",
        "            return [cls(record) for record in records]\n",
        "\n",
        "#MyBaseModel.service = service\n",
        "MyBaseModel.bind(credentials=creds, document_id=GOOGLE_SHEETS_DOCUMENT_ID)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "IzTC3pJfTqtQ",
        "outputId": "ca061d5e-6b17-4388-8dff-c820b192a381"
      },
      "execution_count": 78,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "SPREADSHEET SERVICE...\n",
            "DOCUMENT ID: 1Nk-UBd-3TyWZqbWSvKIIF-S_KKdrkVFRVeNrFy_F1gs\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now all child models will have this capability:"
      ],
      "metadata": {
        "id": "1qs4Ar_-UQd1"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "class MyBook(MyBaseModel):\n",
        "\n",
        "    SHEET_NAME = \"books\"\n",
        "\n",
        "    COLUMNS = [\"title\", \"author\", \"year\"]"
      ],
      "metadata": {
        "id": "i7yBjzcWTP5Y"
      },
      "execution_count": 79,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "books_df = MyBook.all(as_df=True)\n",
        "books_df.head()"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 255
        },
        "id": "W82SswmAQC5R",
        "outputId": "1e93e884-cdf0-443f-d51d-a3fac0df8fc2"
      },
      "execution_count": 80,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "GET SHEET ('books')...\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "    id                   title               author  year  \\\n",
              "id                                                          \n",
              "1    1   To Kill a Mockingbird           Harper Lee  1960   \n",
              "2    2                    1984        George Orwell  1949   \n",
              "3    3        The Great Gatsby  F. Scott Fitzgerald  1925   \n",
              "4    4  The Catcher in the Rye        J.D. Salinger  1951   \n",
              "5    5     Pride and Prejudice          Jane Austen  1813   \n",
              "\n",
              "                         created_at  \n",
              "id                                   \n",
              "1  2024-05-23 02:30:33.559636+00:00  \n",
              "2  2024-05-23 02:30:33.561128+00:00  \n",
              "3  2024-05-23 02:30:33.561181+00:00  \n",
              "4  2024-05-23 02:30:33.561222+00:00  \n",
              "5  2024-05-23 02:30:33.561261+00:00  "
            ],
            "text/html": [
              "\n",
              "  <div id=\"df-2640bc14-7381-4acc-aee0-ae31361220b1\" class=\"colab-df-container\">\n",
              "    <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>title</th>\n",
              "      <th>author</th>\n",
              "      <th>year</th>\n",
              "      <th>created_at</th>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>id</th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>1</td>\n",
              "      <td>To Kill a Mockingbird</td>\n",
              "      <td>Harper Lee</td>\n",
              "      <td>1960</td>\n",
              "      <td>2024-05-23 02:30:33.559636+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>2</td>\n",
              "      <td>1984</td>\n",
              "      <td>George Orwell</td>\n",
              "      <td>1949</td>\n",
              "      <td>2024-05-23 02:30:33.561128+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>3</td>\n",
              "      <td>The Great Gatsby</td>\n",
              "      <td>F. Scott Fitzgerald</td>\n",
              "      <td>1925</td>\n",
              "      <td>2024-05-23 02:30:33.561181+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>4</td>\n",
              "      <td>The Catcher in the Rye</td>\n",
              "      <td>J.D. Salinger</td>\n",
              "      <td>1951</td>\n",
              "      <td>2024-05-23 02:30:33.561222+00:00</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>5</th>\n",
              "      <td>5</td>\n",
              "      <td>Pride and Prejudice</td>\n",
              "      <td>Jane Austen</td>\n",
              "      <td>1813</td>\n",
              "      <td>2024-05-23 02:30:33.561261+00:00</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>\n",
              "    <div class=\"colab-df-buttons\">\n",
              "\n",
              "  <div class=\"colab-df-container\">\n",
              "    <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-2640bc14-7381-4acc-aee0-ae31361220b1')\"\n",
              "            title=\"Convert this dataframe to an interactive table.\"\n",
              "            style=\"display:none;\">\n",
              "\n",
              "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
              "    <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
              "  </svg>\n",
              "    </button>\n",
              "\n",
              "  <style>\n",
              "    .colab-df-container {\n",
              "      display:flex;\n",
              "      gap: 12px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert {\n",
              "      background-color: #E8F0FE;\n",
              "      border: none;\n",
              "      border-radius: 50%;\n",
              "      cursor: pointer;\n",
              "      display: none;\n",
              "      fill: #1967D2;\n",
              "      height: 32px;\n",
              "      padding: 0 0 0 0;\n",
              "      width: 32px;\n",
              "    }\n",
              "\n",
              "    .colab-df-convert:hover {\n",
              "      background-color: #E2EBFA;\n",
              "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "      fill: #174EA6;\n",
              "    }\n",
              "\n",
              "    .colab-df-buttons div {\n",
              "      margin-bottom: 4px;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert {\n",
              "      background-color: #3B4455;\n",
              "      fill: #D2E3FC;\n",
              "    }\n",
              "\n",
              "    [theme=dark] .colab-df-convert:hover {\n",
              "      background-color: #434B5C;\n",
              "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
              "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
              "      fill: #FFFFFF;\n",
              "    }\n",
              "  </style>\n",
              "\n",
              "    <script>\n",
              "      const buttonEl =\n",
              "        document.querySelector('#df-2640bc14-7381-4acc-aee0-ae31361220b1 button.colab-df-convert');\n",
              "      buttonEl.style.display =\n",
              "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "\n",
              "      async function convertToInteractive(key) {\n",
              "        const element = document.querySelector('#df-2640bc14-7381-4acc-aee0-ae31361220b1');\n",
              "        const dataTable =\n",
              "          await google.colab.kernel.invokeFunction('convertToInteractive',\n",
              "                                                    [key], {});\n",
              "        if (!dataTable) return;\n",
              "\n",
              "        const docLinkHtml = 'Like what you see? Visit the ' +\n",
              "          '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
              "          + ' to learn more about interactive tables.';\n",
              "        element.innerHTML = '';\n",
              "        dataTable['output_type'] = 'display_data';\n",
              "        await google.colab.output.renderOutput(dataTable, element);\n",
              "        const docLink = document.createElement('div');\n",
              "        docLink.innerHTML = docLinkHtml;\n",
              "        element.appendChild(docLink);\n",
              "      }\n",
              "    </script>\n",
              "  </div>\n",
              "\n",
              "\n",
              "<div id=\"df-727b9da9-62b5-4718-ab37-54d9321e4e92\">\n",
              "  <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-727b9da9-62b5-4718-ab37-54d9321e4e92')\"\n",
              "            title=\"Suggest charts\"\n",
              "            style=\"display:none;\">\n",
              "\n",
              "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
              "     width=\"24px\">\n",
              "    <g>\n",
              "        <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
              "    </g>\n",
              "</svg>\n",
              "  </button>\n",
              "\n",
              "<style>\n",
              "  .colab-df-quickchart {\n",
              "      --bg-color: #E8F0FE;\n",
              "      --fill-color: #1967D2;\n",
              "      --hover-bg-color: #E2EBFA;\n",
              "      --hover-fill-color: #174EA6;\n",
              "      --disabled-fill-color: #AAA;\n",
              "      --disabled-bg-color: #DDD;\n",
              "  }\n",
              "\n",
              "  [theme=dark] .colab-df-quickchart {\n",
              "      --bg-color: #3B4455;\n",
              "      --fill-color: #D2E3FC;\n",
              "      --hover-bg-color: #434B5C;\n",
              "      --hover-fill-color: #FFFFFF;\n",
              "      --disabled-bg-color: #3B4455;\n",
              "      --disabled-fill-color: #666;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart {\n",
              "    background-color: var(--bg-color);\n",
              "    border: none;\n",
              "    border-radius: 50%;\n",
              "    cursor: pointer;\n",
              "    display: none;\n",
              "    fill: var(--fill-color);\n",
              "    height: 32px;\n",
              "    padding: 0;\n",
              "    width: 32px;\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart:hover {\n",
              "    background-color: var(--hover-bg-color);\n",
              "    box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
              "    fill: var(--button-hover-fill-color);\n",
              "  }\n",
              "\n",
              "  .colab-df-quickchart-complete:disabled,\n",
              "  .colab-df-quickchart-complete:disabled:hover {\n",
              "    background-color: var(--disabled-bg-color);\n",
              "    fill: var(--disabled-fill-color);\n",
              "    box-shadow: none;\n",
              "  }\n",
              "\n",
              "  .colab-df-spinner {\n",
              "    border: 2px solid var(--fill-color);\n",
              "    border-color: transparent;\n",
              "    border-bottom-color: var(--fill-color);\n",
              "    animation:\n",
              "      spin 1s steps(1) infinite;\n",
              "  }\n",
              "\n",
              "  @keyframes spin {\n",
              "    0% {\n",
              "      border-color: transparent;\n",
              "      border-bottom-color: var(--fill-color);\n",
              "      border-left-color: var(--fill-color);\n",
              "    }\n",
              "    20% {\n",
              "      border-color: transparent;\n",
              "      border-left-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "    }\n",
              "    30% {\n",
              "      border-color: transparent;\n",
              "      border-left-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "      border-right-color: var(--fill-color);\n",
              "    }\n",
              "    40% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "      border-top-color: var(--fill-color);\n",
              "    }\n",
              "    60% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "    }\n",
              "    80% {\n",
              "      border-color: transparent;\n",
              "      border-right-color: var(--fill-color);\n",
              "      border-bottom-color: var(--fill-color);\n",
              "    }\n",
              "    90% {\n",
              "      border-color: transparent;\n",
              "      border-bottom-color: var(--fill-color);\n",
              "    }\n",
              "  }\n",
              "</style>\n",
              "\n",
              "  <script>\n",
              "    async function quickchart(key) {\n",
              "      const quickchartButtonEl =\n",
              "        document.querySelector('#' + key + ' button');\n",
              "      quickchartButtonEl.disabled = true;  // To prevent multiple clicks.\n",
              "      quickchartButtonEl.classList.add('colab-df-spinner');\n",
              "      try {\n",
              "        const charts = await google.colab.kernel.invokeFunction(\n",
              "            'suggestCharts', [key], {});\n",
              "      } catch (error) {\n",
              "        console.error('Error during call to suggestCharts:', error);\n",
              "      }\n",
              "      quickchartButtonEl.classList.remove('colab-df-spinner');\n",
              "      quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
              "    }\n",
              "    (() => {\n",
              "      let quickchartButtonEl =\n",
              "        document.querySelector('#df-727b9da9-62b5-4718-ab37-54d9321e4e92 button');\n",
              "      quickchartButtonEl.style.display =\n",
              "        google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
              "    })();\n",
              "  </script>\n",
              "</div>\n",
              "\n",
              "    </div>\n",
              "  </div>\n"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "dataframe",
              "variable_name": "books_df",
              "repr_error": "cannot insert id, already exists"
            }
          },
          "metadata": {},
          "execution_count": 80
        }
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}