{
 "cells": [
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2026-05-11T23:28:16.185704Z",
     "start_time": "2026-05-11T23:28:16.181733Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import requests\n",
    "from urllib.parse import quote_plus\n",
    "\n",
    "GSHEET_ID = \"1Rxdqyo9KPnqgcMzbB9Xl-ITelIpzZSzVfKeSe_Q71cI\"\n",
    "GSHEET_KEY = \"AIzaSyC_wP19S_k0q3T0muYUqN0jRDxwvbsQMQQ\"\n",
    "\n",
    "def sheets_get_tab(tab):\n",
    "    \"\"\"\n",
    "    Fetches data from a Google Sheet tab and returns it as a list of dictionaries.\n",
    "\n",
    "    Args:\n",
    "        tab (str): The name of the sheet tab to fetch.\n",
    "\n",
    "    Returns:\n",
    "        list: A list of dictionaries where each dictionary represents a row with headers as keys.\n",
    "              Returns an empty list if the request fails or data is invalid.\n",
    "    \"\"\"\n",
    "    url = f\"https://sheets.googleapis.com/v4/spreadsheets/{quote_plus(GSHEET_ID)}/values/{quote_plus(tab)}?key={quote_plus(GSHEET_KEY)}\"\n",
    "\n",
    "    try:\n",
    "        response = requests.get(url, timeout=6)\n",
    "        response.raise_for_status()  # Raises HTTPError for bad responses (4xx, 5xx)\n",
    "        data = response.json()\n",
    "    except (requests.exceptions.RequestException, ValueError):\n",
    "        return []\n",
    "\n",
    "    rows = data.get('values', [])\n",
    "    if len(rows) < 2:\n",
    "        return []\n",
    "\n",
    "    headers = [h.strip() for h in rows[0]]\n",
    "    out = []\n",
    "    for row in rows[1:]:\n",
    "        n = len(headers)\n",
    "        padded_row = (row + [''] * n)[:n]  # Pad or truncate to match header length\n",
    "        out.append(dict(zip(headers, padded_row)))\n",
    "\n",
    "    return out\n",
    "\n",
    "d= sheets_get_tab(\"users\")\n"
   ],
   "id": "2c10c2a6f95be5d3",
   "outputs": [],
   "execution_count": 2
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2026-05-11T23:29:48.701756Z",
     "start_time": "2026-05-11T23:29:24.396062Z"
    }
   },
   "cell_type": "code",
   "source": "",
   "id": "18c045eba4d73322",
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Traceback (most recent call last):\n",
      "  File \"/home/jules/.local/share/JetBrains/IntelliJIdea2025.2/python-ce/helpers/pydev/_pydevd_bundle/pydevd_comm.py\", line 736, in make_thread_stack_str\n",
      "    append('file=\"%s\" line=\"%s\">' % (make_valid_xml_value(my_file), lineno))\n",
      "                                     ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^\n",
      "  File \"/home/jules/.local/share/JetBrains/IntelliJIdea2025.2/python-ce/helpers/pydev/_pydevd_bundle/pydevd_xml.py\", line 36, in make_valid_xml_value\n",
      "    return s.replace(\"&\", \"&amp;\").replace('<', '&lt;').replace('>', '&gt;').replace('\"', '&quot;')\n",
      "           ^^^^^^^^^\n",
      "AttributeError: 'tuple' object has no attribute 'replace'\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001B[31m---------------------------------------------------------------------------\u001B[39m",
      "\u001B[31mKeyboardInterrupt\u001B[39m                         Traceback (most recent call last)",
      "\u001B[36mCell\u001B[39m\u001B[36m \u001B[39m\u001B[32mIn[6]\u001B[39m\u001B[32m, line 1\u001B[39m\n\u001B[32m----> \u001B[39m\u001B[32m1\u001B[39m d= sheets_get_tab(\u001B[33m\"users\"\u001B[39m)\n",
      "\u001B[36mCell\u001B[39m\u001B[36m \u001B[39m\u001B[32mIn[2]\u001B[39m\u001B[32m, line 18\u001B[39m, in \u001B[36msheets_get_tab\u001B[39m\u001B[34m(tab)\u001B[39m\n\u001B[32m     14\u001B[39m     Returns:\n\u001B[32m     15\u001B[39m         list: A list of dictionaries where each dictionary represents a row \u001B[38;5;28;01mwith\u001B[39;00m headers \u001B[38;5;28;01mas\u001B[39;00m keys.\n\u001B[32m     16\u001B[39m               Returns an empty list \u001B[38;5;28;01mif\u001B[39;00m the request fails \u001B[38;5;28;01mor\u001B[39;00m data \u001B[38;5;28;01mis\u001B[39;00m invalid.\n\u001B[32m     17\u001B[39m     \"\"\"\n\u001B[32m---> \u001B[39m\u001B[32m18\u001B[39m     url = f\"https://sheets.googleapis.com/v4/spreadsheets/{quote_plus(GSHEET_ID)}/values/{quote_plus(tab)}?key={quote_plus(GSHEET_KEY)}\"\n\u001B[32m     19\u001B[39m \n\u001B[32m     20\u001B[39m     \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m     21\u001B[39m         response = requests.get(url, timeout=\u001B[32m6\u001B[39m)\n",
      "\u001B[36mFile \u001B[39m\u001B[32m/usr/lib/python3.13/urllib/parse.py:936\u001B[39m, in \u001B[36mquote_plus\u001B[39m\u001B[34m(string, safe, encoding, errors)\u001B[39m\n\u001B[32m    930\u001B[39m \u001B[38;5;250m\u001B[39m\u001B[33;03m\"\"\"Like quote(), but also replace ' ' with '+', as required for quoting\u001B[39;00m\n\u001B[32m    931\u001B[39m \u001B[33;03mHTML form values. Plus signs in the original string are escaped unless\u001B[39;00m\n\u001B[32m    932\u001B[39m \u001B[33;03mthey are included in safe. It also does not have safe default to '/'.\u001B[39;00m\n\u001B[32m    933\u001B[39m \u001B[33;03m\"\"\"\u001B[39;00m\n\u001B[32m    934\u001B[39m \u001B[38;5;66;03m# Check if ' ' in string, where string may either be a str or bytes.  If\u001B[39;00m\n\u001B[32m    935\u001B[39m \u001B[38;5;66;03m# there are no spaces, the regular quote will produce the right answer.\u001B[39;00m\n\u001B[32m--> \u001B[39m\u001B[32m936\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m ((\u001B[30;43misinstance\u001B[39;49m(string, \u001B[38;5;28mstr\u001B[39m) \u001B[38;5;129;01mand\u001B[39;00m \u001B[33m'\u001B[39m\u001B[33m \u001B[39m\u001B[33m'\u001B[39m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;129;01min\u001B[39;00m string) \u001B[38;5;129;01mor\u001B[39;00m\n\u001B[32m    937\u001B[39m     (\u001B[38;5;28misinstance\u001B[39m(string, \u001B[38;5;28mbytes\u001B[39m) \u001B[38;5;129;01mand\u001B[39;00m \u001B[33mb\u001B[39m\u001B[33m'\u001B[39m\u001B[33m \u001B[39m\u001B[33m'\u001B[39m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;129;01min\u001B[39;00m string)):\n\u001B[32m    938\u001B[39m     \u001B[38;5;28;01mreturn\u001B[39;00m quote(string, safe, encoding, errors)\n\u001B[32m    939\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(safe, \u001B[38;5;28mstr\u001B[39m):\n",
      "\u001B[36mFile \u001B[39m\u001B[32m~/.local/share/JetBrains/IntelliJIdea2025.2/python-ce/helpers/pydev/_pydevd_bundle/pydevd_frame.py:888\u001B[39m, in \u001B[36mPyDBFrame.trace_dispatch\u001B[39m\u001B[34m(self, frame, event, arg)\u001B[39m\n\u001B[32m    885\u001B[39m             stop = \u001B[38;5;28;01mFalse\u001B[39;00m\n\u001B[32m    887\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m plugin_stop:\n\u001B[32m--> \u001B[39m\u001B[32m888\u001B[39m     stopped_on_plugin = \u001B[30;43mplugin_manager\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mstop\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mmain_debugger\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mframe\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mevent\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_args\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mstop_info\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43marg\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mstep_cmd\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m    889\u001B[39m \u001B[38;5;28;01melif\u001B[39;00m stop:\n\u001B[32m    890\u001B[39m     \u001B[38;5;28;01mif\u001B[39;00m is_line:\n",
      "\u001B[36mFile \u001B[39m\u001B[32m~/.local/share/JetBrains/IntelliJIdea2025.2/python-ce/helpers/jupyter_debug/pydev_jupyter_plugin.py:185\u001B[39m, in \u001B[36mstop\u001B[39m\u001B[34m(plugin, pydb, frame, event, args, stop_info, arg, step_cmd)\u001B[39m\n\u001B[32m    183\u001B[39m     frame = suspend_jupyter(main_debugger, thread, frame, step_cmd)\n\u001B[32m    184\u001B[39m     \u001B[38;5;28;01mif\u001B[39;00m frame:\n\u001B[32m--> \u001B[39m\u001B[32m185\u001B[39m         \u001B[30;43mmain_debugger\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43mdo_wait_suspend\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mthread\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mframe\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mevent\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43marg\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m    186\u001B[39m         \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;01mTrue\u001B[39;00m\n\u001B[32m    187\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;01mFalse\u001B[39;00m\n",
      "\u001B[36mFile \u001B[39m\u001B[32m~/.local/share/JetBrains/IntelliJIdea2025.2/python-ce/helpers/pydev/pydevd.py:1196\u001B[39m, in \u001B[36mPyDB.do_wait_suspend\u001B[39m\u001B[34m(self, thread, frame, event, arg, send_suspend_message, is_unhandled_exception)\u001B[39m\n\u001B[32m   1193\u001B[39m         from_this_thread.append(frame_id)\n\u001B[32m   1195\u001B[39m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[38;5;28mself\u001B[39m._threads_suspended_single_notification.notify_thread_suspended(thread_id, stop_reason):\n\u001B[32m-> \u001B[39m\u001B[32m1196\u001B[39m     \u001B[30;43mself\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43m_do_wait_suspend\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43mthread\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mframe\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mevent\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43marg\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43msuspend_type\u001B[39;49m\u001B[30;43m,\u001B[39;49m\u001B[30;43m \u001B[39;49m\u001B[30;43mfrom_this_thread\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n",
      "\u001B[36mFile \u001B[39m\u001B[32m~/.local/share/JetBrains/IntelliJIdea2025.2/python-ce/helpers/pydev/pydevd.py:1211\u001B[39m, in \u001B[36mPyDB._do_wait_suspend\u001B[39m\u001B[34m(self, thread, frame, event, arg, suspend_type, from_this_thread)\u001B[39m\n\u001B[32m   1208\u001B[39m             \u001B[38;5;28mself\u001B[39m._call_mpl_hook()\n\u001B[32m   1210\u001B[39m         \u001B[38;5;28mself\u001B[39m.process_internal_commands()\n\u001B[32m-> \u001B[39m\u001B[32m1211\u001B[39m         \u001B[30;43mtime\u001B[39;49m\u001B[30;43m.\u001B[39;49m\u001B[30;43msleep\u001B[39;49m\u001B[30;43m(\u001B[39;49m\u001B[30;43m0.01\u001B[39;49m\u001B[30;43m)\u001B[39;49m\n\u001B[32m   1213\u001B[39m \u001B[38;5;28mself\u001B[39m.cancel_async_evaluation(get_current_thread_id(thread), \u001B[38;5;28mstr\u001B[39m(\u001B[38;5;28mid\u001B[39m(frame)))\n\u001B[32m   1215\u001B[39m \u001B[38;5;66;03m# process any stepping instructions\u001B[39;00m\n",
      "\u001B[31mKeyboardInterrupt\u001B[39m: "
     ]
    }
   ],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "language": "python",
   "display_name": "Python 3 (ipykernel)"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
