{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c39086e0-4b8f-44de-92ad-eaed486e44dd",
   "metadata": {},
   "source": [
    "`firefly/ntbks/multiple_datasets_tutorial.ipynb`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "44f4b221-ab90-4ae9-b26b-81f3566fabd6",
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "from IPython.display import YouTubeVideo"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d039be1e-f265-4eb3-82a6-5e4f4bbfdd38",
   "metadata": {},
   "source": [
    "A recording of this jupyter notebook in action is available at:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b8f5f94b-faff-4e69-8aa9-2825dd5db901",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "[encoded data, 12692 characters]\n",
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"400\"\n",
       "            height=\"300\"\n",
       "            src=\"https://www.youtube.com/embed/TMq3IvnxGY8\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "            \n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.YouTubeVideo at 0x1050e1400>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "YouTubeVideo(\"TMq3IvnxGY8\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0c9f95ca-28fe-457b-afb9-7471f4d419fb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "don't have phil's colormaps\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import os\n",
    "\n",
    "import sys\n",
    "## ignore this line, you do not need to add this if Firefly is pip installed into your PYTHONPATH\n",
    "sys.path.insert(0,'/Users/agurvich/research/repos/firefly/src')\n",
    "from firefly.data_reader import ArrayReader"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10a8d0c3-94ca-4945-a061-bd1ca2c98ef9",
   "metadata": {},
   "source": [
    "# Tutorial notebook: Managing multiple datasets with Firefly\n",
    "There are two ways to manage multiple datasets with Firefly\n",
    "1. listing multiple entries in startup.json\n",
    "2. creating a \"standalone\" iteration of Firefly\n",
    "\n",
    "1 and 2 can be combined so that visitors to different \"standalone\" iterations of Firefly can select between different sets of multiple datasets using a dropdown see <a href=\"https://agurvich.github.io/firefly_versions\">this example</a>."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbb5f801-043b-49bc-9b8b-d0886336f7e0",
   "metadata": {},
   "source": [
    "## Editing the entries of `startup.json`\n",
    "When the Firefly webapp starts up it looks for a `firefly/static/data/startup.json` file to tell it which dataset to display. If only a single entry is present then it will automatically begin loading that dataset. If multiple entries are listed then it will present the user with a dropdown box to select which dataset to load. See the <a href=\"https://alexbgurvi.ch/Firefly/docs/build/html/data_reader/multiple_datasets.html\">documentation for managing multiple datasets</a> for how to format the `startup.json` file to list multiple entries manually. We provide a method of easily adding datasets to the `startup.json` file using the `write_startup` keyword argument of the `firefly.data_reader.Reader` (sub-)class(es). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a7729b73-d7e3-4c0a-a0e0-fb9a41be0112",
   "metadata": {},
   "outputs": [],
   "source": [
    "## let's create some sample data, a grid of points in a 3d cube\n",
    "my_coords = np.linspace(-10,10,20)\n",
    "xs,ys,zs = np.meshgrid(my_coords,my_coords,my_coords)\n",
    "xs,ys,zs = xs.flatten(),ys.flatten(),zs.flatten()\n",
    "coords = np.array([xs,ys,zs]).T\n",
    "\n",
    "## we'll pick some random field values to demonstrate filtering/colormapping\n",
    "fields = np.random.random(size=xs.size)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c43a58c-6c90-4a08-998e-7dc08f2e2d0b",
   "metadata": {},
   "source": [
    "We'll overwrite whatever file is existing with a new `startup.json` with only 1 entry in it. Then we'll append a second entry. Then we'll create a reader and specify that it should not be added to the `startup.json` file. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "615a47a9-259c-43a3-b29c-ba0f9ed3b286",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "JSONdir is None, defaulting to /Users/agurvich/research/repos/firefly/src/firefly/static/data/Data\n",
      "Make sure each field_array (2) has a field_filter_flag (0), assuming True.\n",
      "Make sure each field_array (2) has a field_colormap_flag (0), assuming True.\n",
      "Make sure each field_array (2) has a field_radius_flag (0), assuming False.\n",
      "Outputting: PGroup_0 - 7999/7999 particles - 0 tracked fields\n",
      "Outputting: PGroup_1 - 8000/8000 particles - 2 tracked fields\n",
      "JSONdir: /Users/agurvich/FakeData -- is not a sub-directory of firefly/static/data. \n",
      "This may produce confusing or inoperable results. As such, we will create a symlink for you when you  dumpToJSON.\n",
      "Make sure each field_array (2) has a field_filter_flag (0), assuming True.\n",
      "Make sure each field_array (2) has a field_colormap_flag (0), assuming True.\n",
      "Make sure each field_array (2) has a field_radius_flag (0), assuming False.\n",
      "Outputting: PGroup_0 - 7999/7999 particles - 0 tracked fields\n",
      "Outputting: PGroup_1 - 8000/8000 particles - 2 tracked fields\n",
      "JSONdir: /Users/agurvich/NullData -- is not a sub-directory of firefly/static/data. \n",
      "This may produce confusing or inoperable results. As such, we will create a symlink for you when you  dumpToJSON.\n",
      "Make sure each field_array (2) has a field_filter_flag (0), assuming True.\n",
      "Make sure each field_array (2) has a field_colormap_flag (0), assuming True.\n",
      "Make sure each field_array (2) has a field_radius_flag (0), assuming False.\n",
      "Outputting: PGroup_0 - 7999/7999 particles - 0 tracked fields\n",
      "Outputting: PGroup_1 - 8000/8000 particles - 2 tracked fields\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/homebrew/Caskroom/miniconda/base/envs/python38/lib/python3.9/site-packages/numpy/core/fromnumeric.py:1970: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.\n",
      "  result = asarray(a).shape\n",
      "/opt/homebrew/Caskroom/miniconda/base/envs/python38/lib/python3.9/site-packages/numpy/core/fromnumeric.py:43: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.\n",
      "  result = getattr(asarray(obj), method)(*args, **kwds)\n",
      "/opt/homebrew/Caskroom/miniconda/base/envs/python38/lib/python3.9/site-packages/numpy/core/fromnumeric.py:1970: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.\n",
      "  result = asarray(a).shape\n",
      "/opt/homebrew/Caskroom/miniconda/base/envs/python38/lib/python3.9/site-packages/numpy/core/fromnumeric.py:43: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.\n",
      "  result = getattr(asarray(obj), method)(*args, **kwds)\n",
      "/opt/homebrew/Caskroom/miniconda/base/envs/python38/lib/python3.9/site-packages/numpy/core/fromnumeric.py:1970: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.\n",
      "  result = asarray(a).shape\n",
      "/opt/homebrew/Caskroom/miniconda/base/envs/python38/lib/python3.9/site-packages/numpy/core/fromnumeric.py:43: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.\n",
      "  result = getattr(asarray(obj), method)(*args, **kwds)\n"
     ]
    }
   ],
   "source": [
    "## initialize an ArrayReader\n",
    "reader = ArrayReader(\n",
    "    coordinates=[coords[:-1],coords], ## pass in two particle groups as a demonstration (just copies of our sample data)\n",
    "    fields=[[],[fields,fields]], ## field data for each particle group, 0 fields for 1 and 2 repeated fields for the other.\n",
    "    write_startup=True) ## overwrite the existing startup.json file\n",
    "\n",
    "## initialize a second ArrayReader\n",
    "fake_reader = ArrayReader(\n",
    "    coordinates=[coords[:-1],coords], ## pass in two particle groups as a demonstration (just copies of our sample data)\n",
    "    fields=[[],[fields,fields]],## field data for each particle group, 0 fields for 1 and 2 repeated fields for the other.\n",
    "    JSONdir=\"FakeData\",\n",
    "    write_startup='append') ## append this entry to the startup.json file if it doesn't already exists\n",
    "\n",
    "## initialize a THIRD ArrayReader\n",
    "null_reader = ArrayReader(\n",
    "    coordinates=[coords[:-1],coords], ## pass in two particle groups as a demonstration (just copies of our sample data)\n",
    "    fields=[[],[fields,fields]],## field data for each particle group, 0 fields for 1 and 2 repeated fields for the other.\n",
    "    JSONdir=\"NullData\",\n",
    "    write_startup=False) ## do not add this reader to the startup.json file"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "824b5f82-72df-45c0-8226-14fb613c6af7",
   "metadata": {},
   "source": [
    "Let's read the content of the `startup.json` file:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "82be986a-84ad-4829-8080-88488e33c0f0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"0\":\"data\\/Data\",\"1\":\"data\\/FakeData\"}"
     ]
    }
   ],
   "source": [
    "!cat /Users/agurvich/research/repos/firefly/src/firefly/static/data/startup.json"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77a41bf6-89a4-42f9-beb0-5d77ecad92b9",
   "metadata": {},
   "source": [
    "Notice that the \"NullData\" `JSONdir` is not listed because we set `write_startup=False`. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a59ab2a2-0812-4dfc-9df8-8f277cd0c7a6",
   "metadata": {},
   "source": [
    "## Creating a standalone iteration of Firefly\n",
    "You can copy the necessary Firefly source files by creating a `Reader` object containing your data and using the `copyFireflySourceToTarget`. \n",
    "We've also included a script that will automatically create a new Github repository and enable GitHub pages so that your data can be visited by users over the internet via URL. \n",
    "For instructions on how to configure this feature and details for copying the Firefly source see the <a href=\"https://alexbgurvi.ch/Firefly/docs/build/html/data_reader/multiple_datasets.html\">documentation for managing multiple datasets</a>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5c1b91ad-28d6-40d9-95ac-52a4077a34f9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['/Users/agurvich/my_Firefly']"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reader.copyFireflySourceToTarget(init_gh_pages=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7944ab9-ed38-40c4-b494-627cd692dd3e",
   "metadata": {},
   "source": [
    "Let's read the contents of the new `my_Firefly` directory:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "7e502018-95e1-495e-965b-a9ce0cda9f11",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "index.html \u001b[34mstatic\u001b[m\u001b[m\n"
     ]
    }
   ],
   "source": [
    "!ls /Users/agurvich/my_Firefly/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "1fb1c73c-c9ca-4e31-b923-97387835986b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mData\u001b[m\u001b[m         startup.json\n"
     ]
    }
   ],
   "source": [
    "!ls /Users/agurvich/my_firefly/static/data/"
   ]
  }
 ],
 "metadata": {
  "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.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
