class_docstr: Engine for Google spreadsheets
class_docstr_tok: ["Engine","for","Google","spreadsheets"]
docstr: Create a new sheet and update the DB catalog.
  
  Since Google Sheets is not a database, uploading a file is slightly different
  from other traditional databases. To create a table with a given name we first
  create a spreadsheet with the contents of the dataframe, and we later update the
  database catalog to add a mapping between the desired table name and the URL of
  the new sheet.
  
  If the table already exists and the user wants it replaced we clear all the
  cells in the existing sheet before uploading the new data. Appending to an
  existing table is not supported because we can't ensure that the schemas match.
docstr_tok: ["Create","a","new","sheet","and","update","the","DB","catalog",".","Since","Google","Sheets","is","not","a","database",",","uploading","a","file","is","slightly","different","from","other","traditional","databases",".","To","create","a","table","with","a","given","name","we","first","create","a","spreadsheet","with","the","contents","of","the","dataframe",",","and","we","later","update","the","database","catalog","to","add","a","mapping","between","the","desired","table","name","and","the","URL","of","the","new","sheet",".","If","the","table","already","exists","and","the","user","wants","it","replaced","we","clear","all","the","cells","in","the","existing","sheet","before","uploading","the","new","data",".","Appending","to","an","existing","table","is","not","supported","because","we","ca","n't","ensure","that","the","schemas","match","."]
code: def df_to_sql(  # pylint: disable=too-many-locals
      cls,
      database: Database,
      table: Table,
      df: pd.DataFrame,
      to_sql_kwargs: dict[str, Any],
  ) -> None:
      """
      Create a new sheet and update the DB catalog.
  
      Since Google Sheets is not a database, uploading a file is slightly different
      from other traditional databases. To create a table with a given name we first
      create a spreadsheet with the contents of the dataframe, and we later update the
      database catalog to add a mapping between the desired table name and the URL of
      the new sheet.
  
      If the table already exists and the user wants it replaced we clear all the
      cells in the existing sheet before uploading the new data. Appending to an
      existing table is not supported because we can't ensure that the schemas match.
      """
      # pylint: disable=import-outside-toplevel
      from shillelagh.backends.apsw.dialects.base import (
          get_adapter_for_table_name,
      )
  
      # grab the existing catalog, if any
      extra = database.get_extra()
      engine_params = extra.setdefault("engine_params", {})
      catalog = engine_params.setdefault("catalog", {})
  
      # sanity checks
      spreadsheet_url = catalog.get(table.table)
      if spreadsheet_url and "if_exists" in to_sql_kwargs:
          if to_sql_kwargs["if_exists"] == "append":
              # no way we're going to append a dataframe to a spreadsheet, that's
              # never going to work
              raise SupersetException(
                  "Append operation not currently supported"
              )
          if to_sql_kwargs["if_exists"] == "fail":
              raise SupersetException("Table already exists")
          if to_sql_kwargs["if_exists"] == "replace":
              pass
  
      # get the Google session from the Shillelagh adapter
      with cls.get_engine(database) as engine:
          with engine.connect() as conn:
              # any GSheets URL will work to get a working session
              adapter = get_adapter_for_table_name(
                  conn,
                  spreadsheet_url or EXAMPLE_GSHEETS_URL,
              )
              session = (
                  adapter._get_session()
              )  # pylint: disable=protected-access
  
      # clear existing sheet, or create a new one
      if spreadsheet_url:
          spreadsheet_id = (
              adapter._spreadsheet_id
          )  # pylint: disable=protected-access
          range_ = (
              adapter._sheet_name
          )  # pylint: disable=protected-access
          url = (
              "https://sheets.googleapis.com/v4/spreadsheets/"
              f"{spreadsheet_id}/values/{range_}:clear"
          )
          cls._do_post(session, url, {})
      else:
          payload = cls._do_post(
              session,
              "https://sheets.googleapis.com/v4/spreadsheets",
              {"properties": {"title": table.table}},
          )
          spreadsheet_id = payload["spreadsheetId"]
          range_ = payload["sheets"][0]["properties"]["title"]
          spreadsheet_url = payload["spreadsheetUrl"]
  
      # insert data
      data = df.fillna("").values.tolist()
      data.insert(0, df.columns.values.tolist())
      body = {
          "range": range_,
          "majorDimension": "ROWS",
          "values": data,
      }
      url = (
          "https://sheets.googleapis.com/v4/spreadsheets/"
          f"{spreadsheet_id}/values/{range_}:append"
      )
      cls._do_post(
          session,
          url,
          body,
          params={"valueInputOption": "USER_ENTERED"},
      )
  
      # update catalog
      catalog[table.table] = spreadsheet_url
      database.extra = json.dumps(extra)
      db.session.add(database)
      db.session.commit()
  
imports: from __future__ import annotations
  import contextlib
  import json
  import logging
  import re
  from re import Pattern
  from typing import Any, TYPE_CHECKING, TypedDict
  import pandas
  from apispec import APISpec
  from apispec.ext.marshmallow import MarshmallowPlugin
  from flask import g
  from flask_babel import gettext
  from marshmallow import fields, Schema
  from marshmallow.exceptions import ValidationError
  from requests import Session
  from sqlalchemy.engine import create_engine
  from sqlalchemy.engine.url import URL
  from superset import db, security_manager
  from superset.constants import PASSWORD_MASK
  from superset.databases.schemas import encrypted_field_properties, EncryptedString
  from superset.db_engine_specs.shillelagh import ShillelaghEngineSpec
  from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
  from superset.exceptions import SupersetException
label_desc: Use image node_id 10 for calling the GSheetsEngineSpec obj's underlying member method code with example usage: obj.df_to_sql(cls, database, table, df, to_sql_kwargs) without return types