{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/Users/jlaura/github/autocnet\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import sys\n",
    "print(os.path.abspath('../'))\n",
    "sys.path.insert(0, os.path.abspath('../'))\n",
    "\n",
    "from functools import reduce\n",
    "import glob\n",
    "import re\n",
    "\n",
    "from autocnet.graph import network\n",
    "from plio.sqlalchemy_json.alchemy import NestedJsonObject\n",
    "from autocnet.utils.utils import find_in_dict\n",
    "\n",
    "import sqlalchemy\n",
    "from sqlalchemy.ext import declarative\n",
    "from sqlalchemy import orm\n",
    "\n",
    "import json\n",
    "import warnings\n",
    "\n",
    "\n",
    "import pvl\n",
    "from pvl._collections import PVLModule\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generate the data\n",
    "The next three cells take a directory of cleaned `.trn` files (extra values like AUTO or OPTIONAL removed) and generate a `data.db` database of ISIS serial number translations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Database population and a declared class\n",
    "\n",
    "Base = declarative.declarative_base()\n",
    "\n",
    "class Translations(Base):\n",
    "    __tablename__ = 'isis_translations'\n",
    "    id = sqlalchemy.Column(sqlalchemy.INTEGER, primary_key=True)\n",
    "    mission = sqlalchemy.Column(sqlalchemy.String)\n",
    "    instrument = sqlalchemy.Column(sqlalchemy.String)\n",
    "    translation = sqlalchemy.Column(NestedJsonObject)\n",
    "    \n",
    "    def __init__(self, mission, instrument, translation):\n",
    "        self.mission = mission\n",
    "        self.instrument = instrument\n",
    "        self.translation = translation\n",
    "\n",
    "class StringToMission(Base):\n",
    "    __tablename__ = 'isis_mission_to_standard'\n",
    "    id = sqlalchemy.Column(sqlalchemy.INTEGER, primary_key=True)\n",
    "    key = sqlalchemy.Column(sqlalchemy.String)\n",
    "    value = sqlalchemy.Column(sqlalchemy.String)\n",
    "    \n",
    "    def __init__(self, key, value):\n",
    "        self.key = key\n",
    "        self.value = value\n",
    "\n",
    "engine = sqlalchemy.create_engine('sqlite:///data.db')\n",
    "Base.metadata.bind = engine\n",
    "Base.metadata.create_all()\n",
    "session = orm.sessionmaker(bind=engine)()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "files = glob.glob('../autocnet/examples/serial_number_translations/*.trn')\n",
    "for f in files:\n",
    "    p = pvl.load(f)\n",
    "    name = os.path.basename(f[:-16])\n",
    "    try:\n",
    "        v = re.findall(\"([a-z, 0-9]*)([A-Z, a-z, 0-9]*)\", name)[0]\n",
    "        mission, instrument = v\n",
    "    except:\n",
    "        v = re.findall(\"[a-z, 0-9]*\", name)\n",
    "        mission = v[0]\n",
    "        instrument = None\n",
    "    r = Translations(mission, instrument, p)\n",
    "    session.add(r)\n",
    "session.commit()    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Build the mission names lookup table\n",
    "v = \"\"\"\n",
    "Group = MissionName\n",
    "  InputKey      = SpacecraftName\n",
    "  InputGroup    = \"IsisCube,Instrument\"\n",
    "  InputPosition = (IsisCube, Instrument)\n",
    "  Translation   = (Aircraft, \"Aircraft\")\n",
    "  Translation   = (Apollo15, \"APOLLO 15\")\n",
    "  Translation   = (Apollo15, \"APOLLO15\")\n",
    "  Translation   = (Apollo16, \"APOLLO 16\")\n",
    "  Translation   = (Apollo16, \"APOLLO16\")\n",
    "  Translation   = (Apollo17, \"APOLLO 17\")\n",
    "  Translation   = (Apollo17, \"APOLLO17\")\n",
    "  Translation   = (Cassini, Cassini-Huygens)\n",
    "#  Translation   = (Chan1, \"CHANDRAYAAN-1 ORBITER\")\n",
    "#  Translation   = (Chan1, CHANDRAYAAN1_ORBITER)\n",
    "#  Translation   = (Chan1, CHANDRAYAAN-1)\n",
    "  Translation   = (Chandrayaan1, \"CHANDRAYAAN-1 ORBITER\")\n",
    "  Translation   = (Chandrayaan1, CHANDRAYAAN1_ORBITER)\n",
    "  Translation   = (Chandrayaan1, CHANDRAYAAN-1)\n",
    "  Translation   = (Clementine1, CLEMENTINE_1)\n",
    "  Translation   = (Clementine1, \"CLEMENTINE 1\")\n",
    "  Translation   = (Dawn, \"DAWN\")\n",
    "  Translation   = (Galileo, \"Galileo Orbiter\")\n",
    "  Translation   = (Hayabusa, HAYABUSA)\n",
    "  Translation   = (Ideal, IdealSpacecraft)\n",
    "  Translation   = (Kaguya, KAGUYA)\n",
    "  Translation   = (Kaguya, SELENE-M)\n",
    "  Translation   = (Lo, \"Lunar Orbiter 3\")\n",
    "  Translation   = (Lo, \"Lunar Orbiter 4\")\n",
    "  Translation   = (Lo, \"Lunar Orbiter 5\")\n",
    "  Translation   = (Lro, \"LUNAR RECONNAISSANCE ORBITER\")\n",
    "  Translation   = (Lro, \"Lunar Reconnaissance Orbiter\")\n",
    "  Translation   = (Mariner10, Mariner_10)\n",
    "  Translation   = (Mariner10, MARINER_10)\n",
    "  Translation   = (Mer, \"MARS EXPLORATION ROVER 1\")\n",
    "  Translation   = (Mer, MARS_EXPLORATION_ROVER_1)\n",
    "  Translation   = (Mer, \"MARS EXPLORATION ROVER 2\")\n",
    "  Translation   = (Mer, \"SIMULATED MARS EXPLORATION ROVER 1\")\n",
    "  Translation   = (Mer, \"SIMULATED MARS EXPLORATION ROVER 2\")\n",
    "  Translation   = (Messenger, MESSENGER)\n",
    "  Translation   = (Messenger, Messenger)\n",
    "  Translation   = (Mex, \"MARS EXPRESS\")\n",
    "  Translation   = (Mex, \"Mars Express\")\n",
    "    Translation   = (Mgs, MARSGLOBALSURVEYOR)\n",
    "  Translation   = (Mgs, \"MARS GLOBAL SURVEYOR\")\n",
    "  Translation   = (Mro, \"MARS RECONNAISSANCE ORBITER\")\n",
    "  Translation   = (Mro, Mars_Reconnaissance_Orbiter)\n",
    "  Translation   = (NewHorizons, \"NEW HORIZONS\")\n",
    "  Translation   = (Near, NEAR)\n",
    "  Translation   = (Near, \"NEAR EARTH ASTEROID RENDEZVOUS\")\n",
    "  Translation   = (Odyssey, MARS_ODYSSEY)\n",
    "  Translation   = (OsirisRex, OSIRIS-REX)\n",
    "  Translation   = (Smart1, SMART1)\n",
    "  Translation   = (Viking1, VIKING_ORBITER_1)\n",
    "  Translation   = (Viking2, VIKING_ORBITER_2)\n",
    "  Translation   = (Voyager1, VOYAGER_1)\n",
    "  Translation   = (Voyager2, VOYAGER_2)\n",
    "End_Group\n",
    "End\n",
    "\"\"\"\n",
    "p = pvl.loads(v)\n",
    "\n",
    "for k, v in p['MissionName'].items():\n",
    "    if k == 'Translation':\n",
    "        r = StringToMission(v[1], v[0])\n",
    "        session.add(r)\n",
    "session.commit()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sample Query"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Sample querying the database\n",
    "for i, j, t in session.query(Translations.mission, Translations.instrument, Translations.translation):\n",
    "    print(i,j)\n",
    "    d = PVLModule(t)\n",
    "    print(d)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Testing code to prototype the functionality now in autocnet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SpacecraftName ['IsisCube', 'Instrument'] {'*': 'APOLLO15'}\n",
      "InstrumentId ['IsisCube', 'Instrument'] {'*': '*'}\n",
      "StartTime ['IsisCube', 'Instrument'] {'*': '*'}\n",
      "APOLLO15/METRIC/1971-07-31T01:24:36.970\n"
     ]
    }
   ],
   "source": [
    " class SerialNumberDecoder(pvl.decoder.PVLDecoder):\n",
    "     \"\"\"\n",
    "     A PVL Decoder class to handle cube label parsing for the purpose of creating a valid ISIS\n",
    "     serial number. Inherits from the PVLDecoder in planetarypy's pvl module. \n",
    "     \"\"\"\n",
    "     def cast_unquoated_string(self, value):\n",
    "         \"\"\"\n",
    "         Overrides the parent class's method so that any un-quoted string type value found in the\n",
    "         parsed pvl will just return the original value. This is needed so that keyword values\n",
    "         are not re-formatted from what is originally in the ISIS cube label.\n",
    " \n",
    "         Note: This affects value types that are recognized as null, boolean, number, datetime, \n",
    "         et at.\n",
    "         \"\"\"\n",
    "         return value.decode('utf-8')\n",
    "\n",
    "    \n",
    "def get_isis_translation(label):\n",
    "    \"\"\"\n",
    "    Compute the ISIS serial number for a given image using\n",
    "    the input cube or the label extracted from the cube.\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    label : dict or str\n",
    "            A PVL dict object or file name to extract \n",
    "            the PVL object from\n",
    "    \n",
    "    Returns\n",
    "    -------\n",
    "    translation : dict\n",
    "                  A PVLModule object containing the extracted\n",
    "                  translation file\n",
    "    \"\"\"\n",
    "    \n",
    "    if not isinstance(label, PVLModule):\n",
    "        label = pvl.load(label)\n",
    "        \n",
    "    cube_obj = find_in_dict(label, 'Instrument')\n",
    "    \n",
    "    # Grab the spacecraft name and run it through the ISIS lookup\n",
    "    spacecraft_name = find_in_dict(cube_obj, 'SpacecraftName')\n",
    "    for row in session.query(StringToMission).filter(StringToMission.key==spacecraft_name):\n",
    "        spacecraft_name = row.value.lower()\n",
    "    \n",
    "    #Try and pull an instrument identifier\n",
    "    try:\n",
    "        instrumentid = find_in_dict(cube_obj, 'InstrumentId').capitalize()\n",
    "    except:\n",
    "        instrumentid = None\n",
    "        \n",
    "    # Grab the translation PVL object using the lookup\n",
    "    for row in session.query(Translations).filter(Translations.mission==spacecraft_name,\n",
    "                                                  Translations.instrument==instrumentid):\n",
    "        # Convert the JSON back to a PVL object\n",
    "        translation = PVLModule(row.translation)\n",
    "    \n",
    "    return translation\n",
    "\n",
    "def extract_subgroup(data, key_list):\n",
    "    return reduce(lambda d, k: d[k], key_list, data)\n",
    "\n",
    "def generate_serial_number(label):\n",
    "    \n",
    "    if not isinstance(label, PVLModule):\n",
    "        label = pvl.load(label, cls=SerialNumberDecoder)\n",
    "    \n",
    "    # Get the translation information\n",
    "    translation = get_isis_translation(label)\n",
    "    serial_number = []\n",
    "    \n",
    "    # Sort the keys to ensure proper iteration order\n",
    "    keys = sorted(translation.keys())\n",
    "    for k in keys:\n",
    "        group = translation[k]\n",
    "        search_key = group['InputKey']\n",
    "        search_position = group['InputPosition']\n",
    "        search_translation = {group['Translation'][1]:group['Translation'][0]}\n",
    "        print(search_key, search_position, search_translation)\n",
    "        \n",
    "        sub_group = extract_subgroup(label, search_position)\n",
    "        serial_entry = sub_group[search_key]\n",
    "        if serial_entry in search_translation.keys():\n",
    "            serial_entry = search_translation[serial_entry]\n",
    "        elif '*' in search_translation.keys() and search_translation['*'] != '*':\n",
    "            serial_entry = search_translation['*']\n",
    "        \n",
    "        serial_number.append(serial_entry)\n",
    "    \n",
    "    return '/'.join(serial_number)\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Edit below code for the correct path to an Apollo cube and uncomment to test. Must remain commented out for a non-failing build server-side."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#serial = generate_serial_number('/Users/jlaura/Desktop/Apollo15/AS15-M-0296_sub4.cub') \n",
    "#print(serial)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
