{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "151f865d",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%javascript\n",
    "$('#header-container').hide();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79402e24",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import HTML, display\n",
    "\n",
    "import urllib.parse as urlparse\n",
    "import ipyvuetify as v\n",
    "import re"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54ceac75",
   "metadata": {},
   "outputs": [],
   "source": [
    "url = jupyter_notebook_url\n",
    "query_parameters = urlparse.parse_qs(urlparse.urlparse(url).query)\n",
    "\n",
    "workbook_id = query_parameters['workbookId'][0] if 'workbookId' in query_parameters else None\n",
    "condition_id = query_parameters['conditionId'][0] if 'conditionId' in query_parameters else None\n",
    "\n",
    "if not workbook_id or not condition_id:\n",
    "    raise Exception('Workbook and Worksheet IDs must be supplied as query parameters in the URL for this Notebook')\n",
    "\n",
    "# This variable must be set according to the location and name of your Email Notifier notebook.\n",
    "# Typical installation via the Install script will put all of the notebooks in the same location, \n",
    "# so this is assumed for a customization-free installation.\n",
    "notifier_notebook_url = re.sub(r'\\/(notebooks|apps|addon)\\/(.*)\\/.+$', r'/\\1/\\2/Email%20Notifier.ipynb', jupyter_notebook_url)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61d97d2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Unsubscriber:\n",
    "\n",
    "    v.theme.themes.light.success = '#007960'\n",
    "    v.theme.themes.light.primary = '#007960'\n",
    "    v.theme.themes.light.info = '#2a5c84'\n",
    "\n",
    "    # The character limit on job indices is too small to use guids - use a shortened guid instead.\n",
    "    def short_id(self, guid):\n",
    "        return guid[-12:]\n",
    "    \n",
    "    def on_error(self, e):\n",
    "        message = str(e.message) if hasattr(e, 'message') else str(e)\n",
    "        \n",
    "        self.snackbar.color = 'red darken-2'\n",
    "        self.snackbar.children = ['Something went wrong: ' + message, self.close_snackbar]\n",
    "        self.snackbar.timeout = 5000\n",
    "        self.snackbar.v_model = True\n",
    "\n",
    "    def on_success(self, message):\n",
    "        self.snackbar.color = 'success'\n",
    "        self.snackbar.children = [message, self.close_snackbar]\n",
    "        self.snackbar.timeout = 5000\n",
    "        self.snackbar.v_model = True\n",
    "    \n",
    "    def on_close_snackbar(self, widget, event, data):\n",
    "        self.snackbar.v_model = False\n",
    "    \n",
    "    def on_unsubscribe(self, widget, event, data):\n",
    "        self.unsubscribe_button.loading = True\n",
    "        try:\n",
    "            jobs_df = spy.jobs.pull(datalab_notebook_url=notifier_notebook_url, label=self.workbook_id, all=True)\n",
    "            if jobs_df is None or jobs_df.empty or self.short_id(self.condition_id) not in jobs_df.index:\n",
    "                self.unsubscribe_button.loading = False\n",
    "                self.on_error(RuntimeError(f'This notification is no longer scheduled for Workbook ID' \n",
    "                                                  f'{self.workbook_id} and Condition ID {self.condition_id}'))\n",
    "                return\n",
    "            referenced_job = jobs_df.loc[self.short_id(self.condition_id)]\n",
    "            all_address_fields_empty = True\n",
    "            for address_type in ['To', 'Cc', 'Bcc']:\n",
    "                if not address_type in referenced_job:\n",
    "                    continue\n",
    "                addresses_of_type = referenced_job[address_type].split(',')\n",
    "                addresses_to_remove = [address.strip() for address in self.email_addresses.v_model.split(',')]\n",
    "                updated_addresses = [updated_address.strip() for updated_address in addresses_of_type\n",
    "                                     if updated_address.strip() not in addresses_to_remove]\n",
    "                address_update = ','.join(updated_addresses)\n",
    "                if address_update:\n",
    "                    all_address_fields_empty = False\n",
    "                jobs_df.loc[self.short_id(self.condition_id), address_type] = address_update\n",
    "\n",
    "            if all_address_fields_empty:\n",
    "                jobs_df.drop(index=self.short_id(self.condition_id), inplace=True)\n",
    "            spy.jobs.push(\n",
    "                jobs_df,\n",
    "                datalab_notebook_url=notifier_notebook_url,\n",
    "                label=self.workbook_id,\n",
    "                quiet = True\n",
    "            )\n",
    "            self.unsubscribe_button.loading = False\n",
    "            unscheduled_affix = '.  No recipients remain, so the notification job was unscheduled.' \\\n",
    "                if all_address_fields_empty else ''\n",
    "            self.on_success(f'Unsubscribed{unscheduled_affix}')\n",
    "        except Exception as e:\n",
    "            self.unsubscribe_button.loading = False\n",
    "            self.on_error(e)\n",
    "            return\n",
    "            \n",
    "    \n",
    "    def __init__(self, workbook_id, condition_id):\n",
    "        self.workbook_id = workbook_id\n",
    "        self.condition_id = condition_id\n",
    "        \n",
    "        self.close_snackbar = v.Btn(color='white', icon=True, children=[v.Icon(children=['mdi-window-close'])])\n",
    "        self.snackbar = v.Snackbar(v_model=False, app=True, shaped=True, children=[])\n",
    "        self.close_snackbar.on_event('click', self.on_close_snackbar)\n",
    "        \n",
    "        self.email_addresses = v.Textarea(\n",
    "            v_model=spy.user.email,\n",
    "            label='Email Address(es)',\n",
    "            hint='Separate email addresses with commas',\n",
    "            rows=1,\n",
    "            auto_grow=True\n",
    "        )\n",
    "        \n",
    "        self.unsubscribe_button = v.Btn(children=['Unsubscribe'], color='success', class_='mx-2')\n",
    "        self.unsubscribe_button.on_event('click', self.on_unsubscribe)\n",
    "        \n",
    "        self.app = v.App(id='unsubscriber')\n",
    "        self.appBar = v.AppBar(\n",
    "            color='white',\n",
    "            dense=True,\n",
    "            children=[v.ToolbarTitle(children=['Unsubscribe from Notification'])]\n",
    "        )\n",
    "        self.footer = v.Toolbar(children=[self.snackbar, v.Spacer(), self.unsubscribe_button])\n",
    "        \n",
    "    def run(self):\n",
    "        display(HTML(\"<style>{ max-width:100% !important; } .container { width:100% !important; } .mdi-checkbox-marked, .mdi-minus-box { color: #007960 !important; } .v-toolbar__content { padding: 0 !important } .v-snack {position: absolute !important;top: -220px;right: 0 !important; left: unset !important;} </style>\"))\n",
    "        self.app.children = [self.appBar, self.email_addresses, self.footer]\n",
    "        return self.app"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc4f9411",
   "metadata": {},
   "outputs": [],
   "source": [
    "unsubscriber = Unsubscriber(workbook_id, condition_id)\n",
    "unsubscriber.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8b8c948",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
