{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Latest Version - Update December 2, 2022\n",
    "## Compatible with Seeq Versions R58+\n",
    "\n",
    "## INSTALLATION INSTRUCTIONS ##\n",
    "## ------------------------- ##\n",
    "# Before Starting : Open up a duplicate copy of this notebook and click the AppMode button to run it in AppMode.  Copy the Appmode URL, as it will be required in Step 3.\n",
    "# 2. Click Cell -> Run All in this notebook.  Then scroll to the bottom of this worksheet to the GUI. \n",
    "# 3. Using the GUI tools dropdown at the bottom of this notebook, locate any previously installed Add-on Tool Management apps and click uninstall.\n",
    "# 4. Fill in all of the fields in the GUI, using the URL Target from Step 1.  Then click install.  \n",
    "#    Suggested settings :\n",
    "#        Name : Add On Tool Management\n",
    "#        Description : Add or Modify Add On Tools\n",
    "#        Icon : fa fa-wrench\n",
    "#        Link-Type : Window\n",
    "#        Window Details : toolbar=0,location=0,scrollbars=0,statusbar=0,menubar=0,resizable=0,height=550,width=420\n",
    "# 5. Within this notebook click Cell -> All Output -> Clear.  Then save this notebook.  It can now be closed.\n",
    "# 6. Return to workbench, REFRESH the page, and confirm the updated Add-on Tool Management UI is available.  The tool\n",
    "#    can be used to tweak the height and width of the window if required using the 'modify' button."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import display, HTML, clear_output\n",
    "import ipyvuetify as vue\n",
    "import ipywidgets as ipw\n",
    "import functools\n",
    "import time\n",
    "from seeq import sdk\n",
    "import itertools\n",
    "\n",
    "users_api = sdk.UsersApi(spy.client)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       "    div#notebook { padding-top:0px !important; }\n",
       "    div#notebook { padding-bottom: 0px !important}\n",
       "    .container { width:100% !important; }\n",
       "    .end_space { min-height:0px !important; }\n",
       "    div.output_subarea.jupyter-widgets-view { max-width: 100%}\n",
       "    .widget-radio-box { flex-direction: row !important;}\n",
       "    .widget-radio-box label{margin:5px;}\n",
       "    .widget-radio-box {margin-bottom:0px !important;}\n",
       "    .p-Widget jupyter-widgets widget-label {margin-left: 75px !important;}\n",
       "    .end_space {line-height: 25px !important;}\n",
       "</style>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<style>\n",
    "    div#notebook { padding-top:0px !important; }\n",
    "    div#notebook { padding-bottom: 0px !important}\n",
    "    .container { width:100% !important; }\n",
    "    .end_space { min-height:0px !important; }\n",
    "    div.output_subarea.jupyter-widgets-view { max-width: 100%}\n",
    "    .widget-radio-box { flex-direction: row !important;}\n",
    "    .widget-radio-box label{margin:5px;}\n",
    "    .widget-radio-box {margin-bottom:0px !important;}\n",
    "    .p-Widget jupyter-widgets widget-label {margin-left: 75px !important;}\n",
    "    .end_space {line-height: 25px !important;}\n",
    "</style>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "## Utility functions ##\n",
    "\n",
    "def widget_to_dict():\n",
    "    _dict = {\n",
    "            \"Name\": _name.value,\n",
    "            \"Description\": _desc.value,\n",
    "            \"Icon\": _icon.value,\n",
    "            \"Target URL\": _url.value,\n",
    "            \"Link Type\": _linkType.value,\n",
    "            \"Window Details\": _windowDetails.value,\n",
    "            \"Sort Key\": _sortKey.value,\n",
    "            \"Reuse Window\": str(_reuseWindow.value).lower() == 'true',\n",
    "            \"Groups\": [x.strip() for x in _groupPermissions.value.split(',')] if _groupPermissions.value else '',\n",
    "            \"Users\": [x.strip() for x in _userPermissions.value.split(',')] if _userPermissions.value else '',\n",
    "    }\n",
    "    \n",
    "    return {k: v for k, v in _dict.items() if v}\n",
    "\n",
    "def update_icon(b):\n",
    "    _icon_out.value = f'<i class=\"{_icon.value} fa-2x\"></i>'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Main Form UI\n",
    "\n",
    "modify_section = ipw.Output()\n",
    "\n",
    "# form fields...\n",
    "_h_spacer = ipw.HTML(value=\"\", layout=ipw.Layout(width='140px'))\n",
    "_style = {'description_width': '125px'}\n",
    "\n",
    "_name = ipw.Text(description='Name',value='New Tool Name', style=_style)\n",
    "_desc = ipw.Text(description='Description',value='New Tool Description', style=_style)\n",
    "_icon = ipw.Text(description='Icon',value='fa fa-magic', style=_style)\n",
    "_url = ipw.Text(description='Target',value='http://www.seeq.com', style=_style)\n",
    "_linkType = ipw.RadioButtons(description='Link Type',options=['window','tab'], style=_style)\n",
    "_icon_out = ipw.HTML(value=f'<i class=\"{_icon.value} fa-2x\"></i>', style=_style)\n",
    "\n",
    "_windowDetails = ipw.Textarea(description='Window Details',\n",
    "                          value='toolbar=0,location=0,scrollbars=0,statusbar=0,menubar=0,resizable=1,height=600,width=450', \n",
    "                          style=_style)\n",
    "\n",
    "_sortKey = ipw.Text(description='Sort Key',value='z', style=_style)\n",
    "_reuseWindow = ipw.Checkbox(value=True, disabled=False, indent=False, layout=ipw.Layout(width='auto'))\n",
    "_groupPermissions = ipw.Text(description='Allowed Groups',value='', style=_style)\n",
    "_userPermissions = ipw.Text(description='Allowed Users',value=spy.user.email, style=_style)\n",
    "\n",
    "_box = ipw.VBox([_name,\n",
    "                 _desc,\n",
    "                 ipw.HBox([_icon,_icon_out]),\n",
    "                 _url,\n",
    "                 _linkType,\n",
    "                 _windowDetails,\n",
    "                 _sortKey,\n",
    "                 ipw.HBox([ipw.Label(value='Reuse Window', layout = ipw.Layout(display='flex', width = '120px', margin= '0 12px 0 0', justify_content='flex-end')), _reuseWindow]),                 \n",
    "                 _groupPermissions,\n",
    "                 _userPermissions\n",
    "                ])\n",
    "\n",
    "with modify_section:\n",
    "    display(_box)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UserInterface:\n",
    "    \n",
    "    snackbar = vue.Snackbar(v_model=None, top=True, right=True)\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.initialize_ui_components()\n",
    "    \n",
    "    def run(self):\n",
    "        display(self.admin_ui if spy.user.is_admin else self.non_admin_ui)\n",
    "\n",
    "    def update_form(self, _):\n",
    "        if self.tools_dropdown.value is None:\n",
    "            return\n",
    "\n",
    "        search_result = spy.addons.search(query = {\"ID\": self.tools_dropdown.value}, quiet=True)\n",
    "\n",
    "        _name.value = search_result['Name'][0]\n",
    "        _desc.value = search_result['Description'][0]\n",
    "        _url.value = search_result['Target URL'][0]\n",
    "        _icon.value = search_result['Icon'][0]\n",
    "        _linkType.value = search_result['Link Type'][0]\n",
    "        _sortKey.value = search_result['Sort Key'][0]\n",
    "        _reuseWindow.value =  bool(search_result['Reuse Window'][0])\n",
    "        _windowDetails.value = search_result['Window Details'][0]\n",
    "        \n",
    "        user_permissions = list(set([self.get_email_from_username(x) for x in search_result['Users'][0] if self.get_email_from_username(x) is not None]))\n",
    "        _userPermissions.value = (', ').join(user_permissions)\n",
    "        _groupPermissions.value = (', ').join(search_result['Groups'][0])\n",
    "        \n",
    "    def initialize_ui_components(self):\n",
    "        # tools dropdown...\n",
    "        self.tools_dropdown = ipw.Dropdown(description=\"Tools\", style = _style, layout=ipw.Layout(margin= '0 0 0 8px'))\n",
    "        self.tools_dropdown.observe(self.update_form, names='value')\n",
    "\n",
    "        # error output\n",
    "        self.error = ipw.Output()\n",
    "\n",
    "        # buttons\n",
    "        self.install_button = ipw.Button(description=\"Install\", button_style='success',layout=ipw.Layout(width='80px'))\n",
    "        self.modify_button = ipw.Button(description=\"Modify\", button_style='success',layout=ipw.Layout(width='80px'))\n",
    "        self.clear_button = ipw.Button(description=\"Reset\", button_style='success',layout=ipw.Layout(width='80px'))\n",
    "        self.uninstall_button = ipw.Button(description=\"Uninstall\", button_style='success',layout=ipw.Layout(width='80px'))\n",
    "        \n",
    "        self.install_button.on_click(self.install_tool)\n",
    "        self.modify_button.on_click(self.modify_tool)\n",
    "        self.uninstall_button.on_click(self.uninstall_tool)\n",
    "        self.clear_button.on_click(self.update_form)\n",
    "        \n",
    "        # intialize dropdown\n",
    "        self.update_tool_dropdown()\n",
    "        \n",
    "    def get_email_from_username(self, guid):\n",
    "        email_search = sdk.UsersApi(spy.client).autocomplete_users_and_groups(query = guid).items\n",
    "        if email_search == []:\n",
    "            self.snackbar.children = [f'Could not locate user {guid}']\n",
    "            self.snackbar.color = 'error'\n",
    "            self.snackbar.v_model = True\n",
    "            return\n",
    "        else:\n",
    "            return email_search[0].email\n",
    "\n",
    "    def get_username_from_email(self, email):\n",
    "        email = email.strip().lower()\n",
    "        users = [user for user in sdk.UsersApi(spy.client).get_users(email_search = email).users if user.email.strip().lower()==email]\n",
    "        if users == []:\n",
    "            self.snackbar.children = [f'Could not locate user for email {email}.']\n",
    "            self.snackbar.color = 'error'\n",
    "            self.snackbar.v_model = True\n",
    "            return users\n",
    "        else:\n",
    "            return [user.username for user in users]\n",
    "        \n",
    "    \n",
    "    @property\n",
    "    def non_admin_ui(self):\n",
    "        _sysadmin = sdk.SystemApi(spy.client).get_administrator_contact_information()\n",
    "        _nonadmin = ipw.HTML(value=f\"<center><span style='color:red;font-size:20px'>Only Admins can Modify or Create Tools.<br>Please contact<br><a href = 'mailto: {_sysadmin.name}'>{_sysadmin.name}</a></span><center>\")\n",
    "        return _nonadmin\n",
    "    \n",
    "    @property\n",
    "    def admin_ui(self):\n",
    "        _icon.observe(update_icon)  # TO DO - where should this go?\n",
    "        return ipw.VBox([\n",
    "            self.tools_dropdown,\n",
    "            modify_section,\n",
    "            self.error,\n",
    "            ipw.HBox([ipw.HTML(value=\"\", layout=ipw.Layout(width='135px')), self.modify_button, self.install_button]),\n",
    "            ipw.HBox([ipw.HTML(value=\"\", layout=ipw.Layout(width='135px')), self.clear_button, self.uninstall_button]),\n",
    "            self.snackbar\n",
    "        ])\n",
    "\n",
    "    def update_tool_dropdown(self):\n",
    "        self.tools_dropdown.options = self.available_tools\n",
    "     \n",
    "    @property\n",
    "    def available_tools(self):\n",
    "        search_results = spy.addons.search(query = {\"Name\":\"*\"}, quiet=True)\n",
    "        return [] if search_results.empty else search_results[['ID', 'Name']].set_index('Name').to_dict()['ID']\n",
    "    \n",
    "    @property\n",
    "    def selected_tool(self):\n",
    "        return spy.addons.search(query = {\"ID\": self.tools_dropdown.value}, quiet=True)\n",
    "    \n",
    "    def modify_tool(self, _):\n",
    "        self.modify_button.description = 'modifying...'\n",
    "        self.modify_button.disabled = True\n",
    "        current_tool = widget_to_dict()\n",
    "        current_tool['ID'] = self.tools_dropdown.value\n",
    "        current_tool.setdefault('Groups', [])\n",
    "        \n",
    "        \n",
    "        if 'Users' not in current_tool.keys():\n",
    "            current_tool['Users'] = []\n",
    "        else:\n",
    "            current_tool['Users'] = list(itertools.chain.from_iterable([self.get_username_from_email(x) for x in current_tool['Users']]))\n",
    "\n",
    "        if not self.snackbar.v_model:\n",
    "            try:\n",
    "                spy.addons.install(tool = current_tool, update_permissions=True, update_tool=True, quiet=True)\n",
    "                self.snackbar.children = [f'\"{current_tool[\"Name\"]}\" updated successfully']\n",
    "                self.snackbar.color = 'success'\n",
    "                self.update_tool_dropdown()\n",
    "                self.snackbar.v_model = True\n",
    "                \n",
    "            except Exception as e:\n",
    "                self.snackbar.children = [str(e)]\n",
    "                self.snackbar.color = 'error'\n",
    "                self.snackbar.v_model = True\n",
    "        self.modify_button.description = 'Modify'\n",
    "        self.modify_button.disabled = False   \n",
    "    \n",
    "    \n",
    "    def install_tool(self, _):\n",
    "        self.install_button.description = 'installing...'\n",
    "        self.install_button.disabled = True\n",
    "        current_tool = widget_to_dict()\n",
    "    \n",
    "        \n",
    "        if 'Users' not in current_tool.keys():\n",
    "            current_tool['Users'] = []\n",
    "        else:\n",
    "            current_tool['Users'] = list(itertools.chain.from_iterable([self.get_username_from_email(x) for x in current_tool['Users']]))      \n",
    "        \n",
    "        if self.snackbar.v_model and 'Could not locate user' in str(self.snackbar.children):\n",
    "            self.snackbar.children = [f'Could not locate user']\n",
    "            self.snackbar.color = 'error'\n",
    "            self.snackbar.v_model = True\n",
    "            return\n",
    "        \n",
    "        if not self.available_tools or _name.value not in self.available_tools.keys():\n",
    "            try:\n",
    "                spy.addons.install(tool = current_tool, quiet=True)\n",
    "                self.snackbar.children = [f'\"{current_tool[\"Name\"]}\"\" successfully installed']\n",
    "                self.snackbar.color = 'success'\n",
    "                self.update_tool_dropdown()\n",
    "                self.tools_dropdown.index = list(self.tools_dropdown.options.keys()).index(_name.value)\n",
    "                self.snackbar.v_model = True\n",
    "            except Exception as e:\n",
    "                self.snackbar.children = [str(e)]\n",
    "                self.snackbar.color = 'error'\n",
    "                self.snackbar.v_model = True\n",
    "        else:\n",
    "            self.snackbar.children = [f'A tool named \"{_name.value}\" already exists.']\n",
    "            self.snackbar.color = 'error'\n",
    "            self.snackbar.v_model = True\n",
    "        self.install_button.description = 'Install'\n",
    "        self.install_button.disabled = False\n",
    "        \n",
    "    def uninstall_tool(self, _):\n",
    "        self.uninstall_button.description = 'uninstalling...'\n",
    "        self.uninstall_button.disabled = True\n",
    "        spy.addons.uninstall(self.selected_tool, quiet = True)\n",
    "        self.snackbar.children = [f'\"{_name.value}\" uninstalled']\n",
    "        self.snackbar.color = 'success'\n",
    "        self.snackbar.v_model = True\n",
    "        self.update_tool_dropdown()\n",
    "        \n",
    "        self.uninstall_button.description = 'Uninstall'\n",
    "        self.uninstall_button.disabled = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3a39e99618c34c87a21e9b047b18879c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Dropdown(description='Tools', layout=Layout(margin='0 0 0 8px'), options={'Add On Tool Manageme…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(vue.ProgressLinear(indeterminate=True, height=10, color='#008a00'))\n",
    "ui = UserInterface()\n",
    "clear_output(wait=False)\n",
    "ui.run()\n",
    "update_icon(_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "hide_input": false,
  "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.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
