diff --git a/dj-course-01/000-Contents.ipynb b/dj-course-01/000-Contents.ipynb new file mode 100644 index 0000000..ff293b0 --- /dev/null +++ b/dj-course-01/000-Contents.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Welcome to DataJoint\n", + "DataJoint for Python 3 is a full-featured relational database programming sublanguage.\n", + "\n", + "It is designed for work with scientific data and computational data pipelines. This tutorial assumes intermediate programming proficiency in Python. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Contents\n", + "\n", + "### 0. Setup \n", + "| | | | |\n", + "|:--|:--|:--|:--|\n", + "| [Set up and Connect](010-Setup.ipynb) |

install datajoint, configure database connection, `dj.config`, authentication, change password, save configuration, secure connection | [Administration](020-Admin.ipynb) |

configure database server, create user accounts, user privileges" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Work with Individual Tables\n", + "| | | | |\n", + "|:--|:--|:--|:--|\n", + "| [Create a Schema](100-Schema.ipynb) |

`dj.schema` | [Define a Table](110-Table.ipynb) |

table class, simple attributes types, primary and secondary attributes, `insert`, `insert1`, `delete`, `describe`.\n", + "| [Fetch](120-Fetch.ipynb)|

`fetch`, `fetch1`, `head`, `tail`, `len` | [Restrict and project](130-Restrict-Project.ipynb) |

`&`, `-`, `.proj`, `AndList`, restricted `delete`.\n", + "| [More Attribute Types](MoreTypes.ipynb) |

`uuid`, `raw`| [Blobs](130-Blobs.ipynb) |

storing complex data \n", + "| [Attachments](135-Attach.ipynb) |

attaching files | [Lookup Tables](135-Lookup.ipynb) |

specifying fixed contents | \n", + "| [External Storage](160-External.ipynb) |

storing blobs and attachments in external filesystems and AWS S3 | [File Management](170-Filepath.ipynb) |

tracking files in an external repository\n", + "| [Adapted Attribute Types](180-AdaptedTypes.ipynb) |

user-defined attribute types | [Redefining Tables](185-Redefine.ipynb) |

`drop`, `alter`\n", + "| [Secondary Indexes](188-SecondaryIndexes.ipynb) |

secondary indexes | [Transactions](190-Transactions.ipynb) |

Transactions \n", + "| [Log](195-Log.ipynb) |

The log table" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Work with Multiple Tables\n", + "| | | | |\n", + "|:--|:--|:--|:--|\n", + "| [Schemas and Modules](200-SchemaModules.ipynb) |

correspondence between schemas and modules | [Dependencies](210-Dependencies.ipynb) |

primary and secondary dependencies, referential constraints, cascading deletes\n", + "| [Work with Existing Schemas](220-Existing.ipynb) |

`dj.list_schemas`, `schema.spawn_missing_classes`, `dj.create_virtual_modules` | [Diagramming](230-Diagramming.ipynb) | `dj.Diagram`, graph algebra, multi-schema databases |\n", + "| [Join and Restrict](240-Join.ipynb) |

`*`, using `proj` to control join | [Aggregate](250-Aggregate.ipynb) |

`.aggr`\n", + "| [Hierarchical Relationships](250-Nested.ipynb) |

modeling hierarchical or nested data | [Dimensional Relationships](255-Dimensional.ipynb) |

modeling dimensional relationships \n", + "| [Master-part Relationships](Master-Part.ipynb) |

modeling master-part relationships | [Specialization Relationships](260-Specialization.ipynb) |

modeling specialization relationships |\n", + "| [Derived Dependencies](270-DerivedDependencies.ipynb) |

dependencies on query expressions | [Dependency Properties](274-DependencyProperties.ipynb) |

`unique` and `nullable` dependencies\n", + "| [Association Relationships](276-Associations.ipynb) |

modeling associations between entities | [Cyclic Relationships](278-Cyclic.ipynb) |

modeling cyclic relationships | \n", + "| [Universal sets](280-U.ipynb) |

`dj.U` in restrictions, aggregations, and joins | [Union](282-Union.ipynb) |

`+` \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Computations\n", + "| | | | |\n", + "|:--|:--|:--|:--|\n", + "| [Populate](300-SchemaModules.ipynb) |

The `populate` method and the `make` callback in `dj.Imported` and `dj.Computed` tables. | [Distributed Computations](310-Distributed) |

Using `populate` with `reserve_jobs=True`\n", + "| [Jobs Table](320-Jobs.ipynb) |

Working with `schema.jobs` table and `dj.kill` | [Master-Part Computations](330-MasterPart.ipynb) |

Computations in a master-part relationship\n", + "| [Computation Parameters](340-Parameters.ipynb) |

Computation parameters and versions | [Key Source](350-KeySource.ipynb) |

Controlling the scope and granularity of computing jobs with `key_source`\n", + "| [Offline Jobs](360-OfflineJobs.ipynb) |

work with no database connection" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. Interfaces & Applications\n", + "| | | | |\n", + "|:--|:--|:--|:--|\n", + "| [Export](410-Export.ipynb) |

exporting data for dataset sharing | [Web GUIs](440-WebGUIs.ipynb) |

wen interfaces\n" + ] + } + ], + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/dj-course-01/010-Setup.ipynb b/dj-course-01/010-Setup.ipynb new file mode 100644 index 0000000..0e6106a --- /dev/null +++ b/dj-course-01/010-Setup.ipynb @@ -0,0 +1,71 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Setup\n", + "\n", + "## Install DataJoint\n", + "To get started you will need to install `datajoint` from PyPi:\n", + "\n", + "```shell\n", + "$ pip install datajoint\n", + "```\n", + "\n", + "Import datajoint and verify its version. It should be 0.12 or higher. It is a common convention to import datajoint as `dj`:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (external.py, line 166)", + "output_type": "error", + "traceback": [ + "Traceback \u001b[0;36m(most recent call last)\u001b[0m:\n", + " File \u001b[1;32m\"/home/dimitri/.local/lib/python3.7/site-packages/IPython/core/interactiveshell.py\"\u001b[0m, line \u001b[1;32m3326\u001b[0m, in \u001b[1;35mrun_code\u001b[0m\n exec(code_obj, self.user_global_ns, self.user_ns)\n", + " File \u001b[1;32m\"\"\u001b[0m, line \u001b[1;32m1\u001b[0m, in \u001b[1;35m\u001b[0m\n import datajoint as dj\n", + " File \u001b[1;32m\"/home/dimitri/dev/datajoint-python/datajoint/__init__.py\"\u001b[0m, line \u001b[1;32m33\u001b[0m, in \u001b[1;35m\u001b[0m\n from .schema import Schema as schema\n", + "\u001b[0;36m File \u001b[0;32m\"/home/dimitri/dev/datajoint-python/datajoint/schema.py\"\u001b[0;36m, line \u001b[0;32m13\u001b[0;36m, in \u001b[0;35m\u001b[0;36m\u001b[0m\n\u001b[0;31m from .external import ExternalMapping\u001b[0m\n", + "\u001b[0;36m File \u001b[0;32m\"/home/dimitri/dev/datajoint-python/datajoint/external.py\"\u001b[0;36m, line \u001b[0;32m166\u001b[0m\n\u001b[0;31m with open(full_path, 'rb')\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "import datajoint as dj" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Adapted-Quantities.ipynb b/notebooks/Adapted-Quantities.ipynb new file mode 100644 index 0000000..83bd4de --- /dev/null +++ b/notebooks/Adapted-Quantities.ipynb @@ -0,0 +1,239 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Adapted Attribute Type for quantities\n", + "\n", + "**Purpose**: demonstrate using `dj.AttributeAdapter` for convenient storage of custom datatypes, e.g. objects from the quantities module." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's create a table with attribute `volume` storing values of type `mL` from the [`quantities`](https://pypi.org/project/quantities/) library." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.12.7\n" + ] + } + ], + "source": [ + "import datajoint as dj\n", + "print(dj.__version__)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# The following is necessary in DataJoint version 0.12.* \n", + "# while adapted types are in beta testing.\n", + "dj.errors._switch_adapted_types(True) " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import quantities as pq\n", + "\n", + "class Milliliter(dj.AttributeAdapter):\n", + " attribute_type = 'float'\n", + " \n", + " def put(self, obj: pq.Quantity) -> float:\n", + " assert isinstance(obj, pq.Quantity)\n", + " obj = obj.rescale(pq.mL)\n", + " return obj.item()\n", + "\n", + " def get(self, value) -> str:\n", + " return value * pq.mL\n", + "\n", + "mL = Milliliter()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we need to define an adapter object that convert target objects into an attribute type that datajoint can already store. The class must subclass `dj.AttributeAdapter` and define the property `attribute_type`, and methods `get` and `put`. These methods translate the adapted data type `nx.Graph` into a representation that can be stored in datajoint, a `longblob` storing the edge list." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can define a table that uses `graph` as its attribute type. These \"adapted types\" must be enclosed in angle brackets as in ``:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dbadmin@dimitri-proj0.cda95qzjbnvs.us-east-1.rds.amazonaws.com:3306\n", + "Proceed to delete entire schema `test_quantities`? [yes, No]: yes\n" + ] + } + ], + "source": [ + "schema = dj.schema('test_quantities')\n", + "schema.drop() # drop previous contents\n", + "schema = dj.schema('test_quantities') # create de novo" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Quantity(dj.Manual):\n", + " definition = \"\"\"\n", + " quant_id : int\n", + " ---\n", + " volume : \n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "quant_id : int \n", + "---\n", + "volume : \n", + "\n" + ] + } + ], + "source": [ + "Quantity.describe();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now, populate the table with our example graphs and fetch them as objects\n", + "Inserting the graphs as objects" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "Quantity().insert([\n", + " (1, 2.3 * pq.mL),\n", + " (2, 3.5 * pq.mL)])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([array(2.3) * mL, array(3.5) * mL], dtype=object)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "q = Quantity.fetch('volume')\n", + "q" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "d = (3.5 * pq.mL).dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('float64')" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Adapted-Types-2.ipynb b/notebooks/Adapted-Types-2.ipynb new file mode 100644 index 0000000..e436022 --- /dev/null +++ b/notebooks/Adapted-Types-2.ipynb @@ -0,0 +1,249 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Adapted Attribute Types with Spawned Classes and Virtual Modules\n", + "\n", + "**Purpose**: demonstrate using `dj.AttributeAdapter` for convenient storage of arbitrary data types in DataJoint table attributes when working with previously defined schemas.\n", + "\n", + "This notebook works with the schema created previously in [Adapted-Types.ipynb](Adapted-Types.ipynb). Please execute it first, leaving the `Connection` table defined and populated.\n", + "\n", + "Also, see [Spawning-Classes.ipynb](Spawning-Classes.ipynb) for a review of `schema.spawn_missing_classes` and `dj.create_virtual_module`. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "import datajoint as dj" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's connect to the schema defined in [Adapted-Types.ipynb](Adapted-Types.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dbadmin@dimitri-proj0.cda95qzjbnvs.us-east-1.rds.amazonaws.com:3306\n" + ] + } + ], + "source": [ + "schema = dj.schema('test_graphs')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To work with the `` type used in that schema we need to define or import the adapter object *before* `spawn_missing_classes`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "class GraphAdapter(dj.AttributeAdapter):\n", + " \n", + " attribute_type = 'longblob' # this is how the attribute will be declared\n", + " \n", + " def put(self, obj):\n", + " # convert the nx.Graph object into an edge list\n", + " assert isinstance(obj, nx.Graph)\n", + " return list(obj.edges)\n", + "\n", + " def get(self, value):\n", + " # convert edge list back into an nx.Graph\n", + " return nx.Graph(value)\n", + " \n", + "\n", + "# instantiate for use as a datajoint type\n", + "graph = GraphAdapter()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Spawning missing classes in the local namespace" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now spawning missing classes will have the type adapter accessible:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "schema.spawn_missing_classes()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'Connectivity' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mmatplotlib\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpyplot\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mConnectivity\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfetch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'conn_graph'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morder_by\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'conn_id'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msubplots\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m \u001b[0;34m,\u001b[0m \u001b[0mfigsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m15\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'Connectivity' is not defined" + ] + } + ], + "source": [ + "%matplotlib inline\n", + "from matplotlib import pyplot as plt\n", + "\n", + "result = Connectivity.fetch('conn_graph', order_by='conn_id')\n", + "\n", + "fig, axx = plt.subplots(1, result.size , figsize=(15, 3))\n", + "for g, ax in zip(result, axx.flatten()):\n", + " plt.sca(ax)\n", + " nx.draw(g)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a virtual module with adapted attribute types\n", + "\n", + "To allow adapted attribyte types in virtual modules, they must be passed into the virtual module using the `add_objects` argument:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "vmod = dj.create_virtual_module('vmod', 'test_graphs', add_objects={'graph': graph})" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "Connectivity\n", + "\n", + "\n", + "Connectivity\n", + "\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(vmod)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/dimitri/.local/lib/python3.7/site-packages/networkx/drawing/nx_pylab.py:579: MatplotlibDeprecationWarning: \n", + "The iterable function was deprecated in Matplotlib 3.1 and will be removed in 3.3. Use np.iterable instead.\n", + " if not cb.iterable(width):\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "

" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = vmod.Connectivity.fetch('conn_graph', order_by='conn_id')\n", + "\n", + "fig, axx = plt.subplots(1, result.size , figsize=(15, 3))\n", + "for g, ax in zip(result, axx.flatten()):\n", + " plt.sca(ax)\n", + " nx.draw(g)" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Adapted-Types.ipynb b/notebooks/Adapted-Types.ipynb new file mode 100644 index 0000000..2d6088d --- /dev/null +++ b/notebooks/Adapted-Types.ipynb @@ -0,0 +1,237 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Adapted Attribute Types\n", + "\n", + "**Purpose**: demonstrate using `dj.AttributeAdapter` for convenient storage of arbitrary data types in DataJoint table attributes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Imagine I want store graph objects of type `networkx.Graph` in the form of edge lists. \n", + "\n", + "First, let's create a few graphs:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "from matplotlib import pyplot as plt\n", + "import networkx as nx\n", + "graphs = [nx.lollipop_graph(4, 2), nx.star_graph(5), nx.barbell_graph(3, 1), nx.cycle_graph(5)]\n", + "\n", + "fig, axx = plt.subplots(1, len(graphs) , figsize=(15, 3))\n", + "for g, ax in zip(graphs, axx.flatten()):\n", + " plt.sca(ax)\n", + " nx.draw(g)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we need to define an adapter object that convert target objects into an attribute type that datajoint can already store. The class must subclass `dj.AttributeAdapter` and define the property `attribute_type`, and methods `get` and `put`. These methods translate the adapted data type `nx.Graph` into a representation that can be stored in datajoint, a `longblob` storing the edge list." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "\n", + "class GraphAdapter(dj.AttributeAdapter):\n", + " \n", + " attribute_type = 'longblob' # this is how the attribute will be declared\n", + " \n", + " def put(self, obj):\n", + " # convert the nx.Graph object into an edge list\n", + " assert isinstance(obj, nx.Graph)\n", + " return list(obj.edges)\n", + "\n", + " def get(self, value):\n", + " # convert edge list back into an nx.Graph\n", + " return nx.Graph(value)\n", + " \n", + "\n", + "# instantiate for use as a datajoint type\n", + "graph = GraphAdapter()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can define a table that uses `graph` as its attribute type. These \"adapted types\" must be enclosed in angle brackets as in ``:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dbadmin@dimitri-proj0.cda95qzjbnvs.us-east-1.rds.amazonaws.com:3306\n", + "Proceed to delete entire schema `test_graphs`? [yes, No]: yes\n" + ] + } + ], + "source": [ + "schema = dj.schema('test_graphs')\n", + "schema.drop() # drop previous contents\n", + "schema = dj.schema('test_graphs') # create de novo" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# The following is necessary in DataJoint version 0.12.* \n", + "# while adapted types are in beta testing.\n", + "dj.errors._switch_adapted_types(True) " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Connectivity(dj.Manual):\n", + " definition = \"\"\"\n", + " conn_id : int\n", + " ---\n", + " conn_graph = null : # a networkx.Graph object \n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "conn_id : int \n", + "---\n", + "conn_graph=null : # a networkx.Graph object\n", + "\n" + ] + } + ], + "source": [ + "Connectivity.describe();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now, populate the table with our example graphs and fetch them as objects\n", + "Inserting the graphs as objects" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "Connectivity.insert((i, g) for i, g in enumerate(graphs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now fetch the graphs as an array of objects and plot them to verify successful recovery." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result = Connectivity.fetch('conn_graph', order_by='conn_id')\n", + "\n", + "fig, axx = plt.subplots(1, result.size, figsize=(15, 3))\n", + "for g, ax in zip(result, axx.flatten()):\n", + " plt.sca(ax)\n", + " nx.draw(g)" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Alter.ipynb b/notebooks/Alter.ipynb index a0ba0bb..a595a1d 100644 --- a/notebooks/Alter.ipynb +++ b/notebooks/Alter.ipynb @@ -42,18 +42,20 @@ { "data": { "image/svg+xml": [ - "\n", + "\n", "\n", "%3\n", - "\n", - "\n", + "\n", + "\n", "\n", - "Term\n", - "Exam\n", + "\n", - "\n", - "Term\n", + "\n", + "Exam\n", "\n", "\n", "\n", @@ -66,143 +68,158 @@ "------------------------------\r", "auditorium           \r", "\">\n", - "\n", - "Section\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Term->Section\n", - "\n", - "\n", - "\n", - "\n", - "CurrentTerm\n", - "\n", - "\n", - "CurrentTerm\n", + "\n", + "Section\n", "\n", "\n", "\n", - "\n", - "\n", - "Term->CurrentTerm\n", - "\n", - "\n", "\n", - "\n", + "\n", "Enroll\n", - "\n", - "\n", - "Enroll\n", + "\n", + "Enroll\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Section->Enroll\n", - "\n", + "\n", "\n", "\n", - "\n", + "\n", "Grade\n", - "\n", - "\n", - "Grade\n", + "\n", + "Grade\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Major\n", + "\n", + "\n", + "Major\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "StudentMajor\n", + "\n", + "\n", + "StudentMajor\n", "\n", "\n", "\n", + "\n", + "\n", + "Enroll->Exam\n", + "\n", + "\n", "\n", - "\n", + "\n", "Enroll->Grade\n", - "\n", + "\n", "\n", - "\n", - "\n", - "Department\n", - "\n", + "\n", + "LetterGrade\n", + "\n", - "\n", - "Department\n", + "\n", + "LetterGrade\n", "\n", "\n", "\n", + "\n", + "\n", + "LetterGrade->Grade\n", + "\n", + "\n", "\n", - "\n", + "\n", "Course\n", - "\n", - "\n", - "Course\n", + "\n", + "Course\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "Department->Course\n", - "\n", + "Course->Section\n", + "\n", "\n", - "\n", + "\n", "\n", - "StudentMajor\n", - "CurrentTerm\n", + "\n", - "\n", - "StudentMajor\n", + "\n", + "CurrentTerm\n", "\n", "\n", "\n", - "\n", - "\n", - "Department->StudentMajor\n", - "\n", - "\n", - "\n", - "\n", - "LetterGrade\n", - "\n", + "\n", + "Department\n", + "\n", - "\n", - "LetterGrade\n", + "\n", + "Department\n", "\n", "\n", "\n", - "\n", + "\n", + "\n", + "Department->Major\n", + "\n", + "\n", + "\n", "\n", - "LetterGrade->Grade\n", - "\n", + "Department->StudentMajor\n", + "\n", "\n", - "\n", + "\n", "\n", - "Course->Section\n", - "\n", + "Department->Course\n", + "\n", "\n", "\n", - "\n", + "\n", "Student\n", - "\n", - "\n", - "Student\n", + "\n", + "Student\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "Student->Enroll\n", - "\n", + "Student->Major\n", + "\n", "\n", "\n", "\n", "Student->StudentMajor\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "Student->Enroll\n", + "\n", + "\n", + "\n", + "\n", + "Term\n", + "\n", + "\n", + "Term\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Term->Section\n", + "\n", + "\n", + "\n", + "\n", + "Term->CurrentTerm\n", + "\n", "\n", "\n", "" ], "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -267,40 +310,10 @@ " \"\"\"" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's change the definition to record the exam date too" - ] - }, { "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], - "source": [ - "@schema\n", - "class Exam(dj.Manual):\n", - " definition = \"\"\"\n", - " -> Enroll\n", - " ---\n", - " exam_date : date\n", - " score : decimal(5,2) # percent of total \n", - " \"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Even though we updated the `definition` in the class, the change is not reflected on the server:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, "outputs": [ { "data": { @@ -378,23 +391,82 @@ "

section

\n", " \n", "
\n", - "

score

\n", + "

exam_date

\n", + " \n", + "
\n", + "

exam_score

\n", " percent of total\n", "
\n", - " \n", + " 1016\n", + "MATH\n", + "2280\n", + "2018\n", + "Fall\n", + "a\n", + "None\n", + "86.0001067\n", + "CS\n", + "2100\n", + "2018\n", + "Fall\n", + "a\n", + "None\n", + "13.0001084\n", + "MATH\n", + "1250\n", + "2018\n", + "Fall\n", + "a\n", + "2019-04-30\n", + "65.0001201\n", + "BIOL\n", + "2021\n", + "2018\n", + "Fall\n", + "a\n", + "None\n", + "22.0001204\n", + "CS\n", + "1030\n", + "2018\n", + "Fall\n", + "d\n", + "2019-04-30\n", + "42.0001484\n", + "PHYS\n", + "2040\n", + "2018\n", + "Fall\n", + "a\n", + "None\n", + "83.0001515\n", + "PHYS\n", + "2140\n", + "2018\n", + "Fall\n", + "a\n", + "None\n", + "13.000 \n", " \n", - " \n", - "

Total: 0

\n", + "

...

\n", + "

Total: 120

\n", " " ], "text/plain": [ - "*student_id *dept *course *term_year *term *section score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +-------+\n", - "\n", - " (Total: 0)" + "*student_id *dept *course *term_year *term *section exam_date exam_score \n", + "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +------------+ +------------+\n", + "1016 MATH 2280 2018 Fall a None 86.000 \n", + "1067 CS 2100 2018 Fall a None 13.000 \n", + "1084 MATH 1250 2018 Fall a 2019-04-30 65.000 \n", + "1201 BIOL 2021 2018 Fall a None 22.000 \n", + "1204 CS 1030 2018 Fall d 2019-04-30 42.000 \n", + "1484 PHYS 2040 2018 Fall a None 83.000 \n", + "1515 PHYS 2140 2018 Fall a None 13.000 \n", + " ...\n", + " (Total: 120)" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -403,646 +475,387 @@ "Exam()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can examine the definition on the server using the `describe` method:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> Enroll\n", - "---\n", - "score : decimal(5,2) # percent of total\n", - "\n" - ] - } - ], - "source": [ - "Exam.describe();" - ] - }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " -> Enroll\n", - " ---\n", - " exam_date : date\n", - " score : decimal(5,2) # percent of total \n", - " \n" - ] - } - ], - "source": [ - "print(Exam.definition)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One solution is to simply drop the table and declare it again, which is fine when it contains no valuable data. But let's consider the case when the table is already populated and we wish to keep the existing data. \n", - "\n", - "First, let's insert some exam entries:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# pick 100 random enrollments from the current term\n", - "import random\n", - "keys = random.sample(((Enroll - Exam) & CurrentTerm).fetch('KEY'), 100)\n", - "# assign random scores\n", - "for key in keys:\n", - " Exam.insert1(dict(key, score=random.randint(0,10000)/100))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can alter the `Exam` table with the new definition:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

score

\n", - " percent of total\n", - "
1069PHYS21002018Falla79.15
1084MATH12502018Falla44.69
1164BIOL10062018Fallb78.62
\n", - "

...

\n", - "

Total: 100

\n", - " " - ], - "text/plain": [ - "*student_id *dept *course *term_year *term *section score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +-------+\n", - "1069 PHYS 2100 2018 Fall a 79.15 \n", - "1084 MATH 1250 2018 Fall a 44.69 \n", - "1164 BIOL 1006 2018 Fall b 78.62 \n", - " ...\n", - " (Total: 100)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Exam()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can now use the `alter` method to apply the new definition:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tADD `exam_date` date NOT NULL AFTER `section`,\n", - "\tMODIFY `score` decimal(5,2) NOT NULL COMMENT \"percent of total\" AFTER `exam_date`\n", - "\n", - "Execute? [yes, no]: yes\n" - ] - }, - { - "ename": "InternalError", - "evalue": "(1292, \"Incorrect date value: '0000-00-00' for column 'exam_date' at row 1\")", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mInternalError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mExam\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0malter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36malter\u001b[0;34m(self, prompt, context)\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstore\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mexternal_stores\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mschemas\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdatabase\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexternal\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstore\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 104\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msql\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 105\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mpymysql\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mOperationalError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merror\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;31m# skip if no create privilege\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/dev/datajoint-python/datajoint/connection.py\u001b[0m in \u001b[0;36mquery\u001b[0;34m(self, query, args, as_dict, suppress_warnings, reconnect)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[0;31m# suppress all warnings arising from underlying SQL library\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msimplefilter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ignore\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 148\u001b[0;31m \u001b[0mcur\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 149\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mInterfaceError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mOperationalError\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 150\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mis_connection_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mreconnect\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/cursors.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, query, args)\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0mquery\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmogrify\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 169\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 170\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_query\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 171\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_executed\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mquery\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 172\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/cursors.py\u001b[0m in \u001b[0;36m_query\u001b[0;34m(self, q)\u001b[0m\n\u001b[1;32m 326\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_last_executed\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mq\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 327\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_clear_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 328\u001b[0;31m \u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 329\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_get_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 330\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrowcount\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36mquery\u001b[0;34m(self, sql, unbuffered)\u001b[0m\n\u001b[1;32m 515\u001b[0m \u001b[0msql\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msql\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'surrogateescape'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 516\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute_command\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mCOMMAND\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCOM_QUERY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msql\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 517\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_affected_rows\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_query_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munbuffered\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0munbuffered\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 518\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_affected_rows\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 519\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36m_read_query_result\u001b[0;34m(self, unbuffered)\u001b[0m\n\u001b[1;32m 730\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 731\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMySQLResult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 732\u001b[0;31m \u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 733\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_result\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 734\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserver_status\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36mread\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1073\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1074\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1075\u001b[0;31m \u001b[0mfirst_packet\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_packet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1076\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1077\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfirst_packet\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_ok_packet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36m_read_packet\u001b[0;34m(self, packet_type)\u001b[0m\n\u001b[1;32m 682\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 683\u001b[0m \u001b[0mpacket\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpacket_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbuff\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 684\u001b[0;31m \u001b[0mpacket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcheck_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 685\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mpacket\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 686\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/protocol.py\u001b[0m in \u001b[0;36mcheck_error\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 218\u001b[0m \u001b[0merrno\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_uint16\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 219\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mDEBUG\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"errno =\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrno\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 220\u001b[0;31m \u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_mysql_exception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 221\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdump\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/err.py\u001b[0m in \u001b[0;36mraise_mysql_exception\u001b[0;34m(data)\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0merrval\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'utf-8'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'replace'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0merrorclass\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0merror_map\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mInternalError\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merrorclass\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrval\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mInternalError\u001b[0m: (1292, \"Incorrect date value: '0000-00-00' for column 'exam_date' at row 1\")" - ] - } - ], - "source": [ - "Exam.alter()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Oh! New attributes cannot be added to tables with existing data without providing a default value. Let's update the definition to allow `exam_date` to be empty (default to `null`) and alter the table again." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "@schema\n", - "class Exam(dj.Manual):\n", - " definition = \"\"\"\n", - " -> Enroll \n", - " ---\n", - " exam_date = null: date \n", - " score : decimal(5,2) # percent of total \n", - " \"\"\"" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tADD `exam_date` date DEFAULT NULL AFTER `section`,\n", - "\tMODIFY `score` decimal(5,2) NOT NULL COMMENT \"percent of total\" AFTER `exam_date`\n", - "\n", - "Execute? [yes, no]: yes\n", - "Table altered\n" - ] - } - ], - "source": [ - "Exam.alter()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

exam_date

\n", - " \n", - "
\n", - "

score

\n", - " percent of total\n", - "
1069PHYS21002018FallaNone79.15
1084MATH12502018FallaNone44.69
1164BIOL10062018FallbNone78.62
\n", - "

...

\n", - "

Total: 100

\n", - " " + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "Exam\n", + "\n", + "\n", + "Exam\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Section\n", + "\n", + "\n", + "Section\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Enroll\n", + "\n", + "\n", + "Enroll\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Section->Enroll\n", + "\n", + "\n", + "\n", + "\n", + "Grade\n", + "\n", + "\n", + "Grade\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Major\n", + "\n", + "\n", + "Major\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "StudentMajor\n", + "\n", + "\n", + "StudentMajor\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Enroll->Exam\n", + "\n", + "\n", + "\n", + "\n", + "Enroll->Grade\n", + "\n", + "\n", + "\n", + "\n", + "LetterGrade\n", + "\n", + "\n", + "LetterGrade\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "LetterGrade->Grade\n", + "\n", + "\n", + "\n", + "\n", + "Course\n", + "\n", + "\n", + "Course\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Course->Section\n", + "\n", + "\n", + "\n", + "\n", + "CurrentTerm\n", + "\n", + "\n", + "CurrentTerm\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Department\n", + "\n", + "\n", + "Department\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Department->Major\n", + "\n", + "\n", + "\n", + "\n", + "Department->StudentMajor\n", + "\n", + "\n", + "\n", + "\n", + "Department->Course\n", + "\n", + "\n", + "\n", + "\n", + "Student\n", + "\n", + "\n", + "Student\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Student->Major\n", + "\n", + "\n", + "\n", + "\n", + "Student->StudentMajor\n", + "\n", + "\n", + "\n", + "\n", + "Student->Enroll\n", + "\n", + "\n", + "\n", + "\n", + "Term\n", + "\n", + "\n", + "Term\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Term->Section\n", + "\n", + "\n", + "\n", + "\n", + "Term->CurrentTerm\n", + "\n", + "\n", + "\n", + "" ], "text/plain": [ - "*student_id *dept *course *term_year *term *section exam_date score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +-----------+ +-------+\n", - "1069 PHYS 2100 2018 Fall a None 79.15 \n", - "1084 MATH 1250 2018 Fall a None 44.69 \n", - "1164 BIOL 1006 2018 Fall b None 78.62 \n", - " ...\n", - " (Total: 100)" + "" ] }, - "execution_count": 13, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Exam()" + "dj.Diagram(schema)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now let's add some grades for today's exam:" + "Let's change the definition to record the exam date too" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "from datetime import datetime\n", - "today = datetime.now().date().isoformat()" + "@schema\n", + "class Exam(dj.Manual):\n", + " definition = \"\"\"\n", + " -> Enroll\n", + " ---\n", + " exam_date : date\n", + " score : decimal(5,2) # percent of total \n", + " \"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Even though we updated the `definition` in the class, the change is not reflected on the server:" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# pick 20 random enrollments from the current term\n", - "keys = random.sample(((Enroll - Exam) & CurrentTerm).fetch('KEY'), 20)\n", - "# assign random scores\n", - "for key in keys:\n", - " Exam.insert1(dict(key, score=random.randint(0,10000)/100, exam_date=today))" + "Exam()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can examine the definition on the server using the `describe` method:" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

exam_date

\n", - " \n", - "
\n", - "

score

\n", - " percent of total\n", - "
1069PHYS21002018FallaNone79.15
1084MATH12502018FallaNone44.69
1088CS31002018Falla2019-04-2317.70
\n", - "

...

\n", - "

Total: 120

\n", - " " - ], - "text/plain": [ - "*student_id *dept *course *term_year *term *section exam_date score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +------------+ +-------+\n", - "1069 PHYS 2100 2018 Fall a None 79.15 \n", - "1084 MATH 1250 2018 Fall a None 44.69 \n", - "1088 CS 3100 2018 Fall a 2019-04-23 17.70 \n", - " ...\n", - " (Total: 120)" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], + "source": [ + "Exam.describe();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ - "Exam()" + "Exam.heading" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now let's say we want to rename `score` into `exam_score`:" + "One solution is to simply drop the table and declare it again, which is fine when it contains no valuable data. But let's consider the case when the table is already populated and we wish to keep the existing data. \n", + "\n", + "First, let's insert some exam entries:" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "@schema\n", - "class Exam(dj.Manual):\n", - " definition = \"\"\"\n", - " -> Enroll \n", - " ---\n", - " exam_date = null: date \n", - " exam_score : decimal(5,2) # percent of total \n", - " \"\"\"" + "# pick 100 random enrollments from the current term\n", + "import random\n", + "keys = random.sample(((Enroll - Exam) & CurrentTerm).fetch('KEY'), 100)\n", + "# assign random scores\n", + "for key in keys:\n", + " Exam.insert1(dict(key, score=random.randint(0,10000)/100))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can alter the `Exam` table with the new definition:" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tDROP `score`,\n", - "\tADD `exam_score` decimal(5,2) NOT NULL COMMENT \"percent of total\" AFTER `exam_date`\n", - "\n", - "Execute? [yes, no]: no\n" - ] - } - ], + "outputs": [], + "source": [ + "Exam()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now use the `alter` method to apply the new definition:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dj.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], "source": [ - "# Say NO!\n", "Exam.alter()" ] }, @@ -1050,14 +863,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note that rather than renaming, `alter` attempted to drop the old attribute and add the new one. \n", - "\n", - "To rename, we must indicate the old attribute name in curly brackets as the first thing in the attribute comment:" + "Oh! New attributes cannot be added to tables with existing data without providing a default value. Let's update the definition to allow `exam_date` to be empty (default to `null`) and alter the table again." ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1067,160 +878,148 @@ " -> Enroll \n", " ---\n", " exam_date = null: date \n", - " exam_score : decimal(5,2) # {score} percent of total \n", + " score : decimal(5,2) # percent of total \n", " \"\"\"" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tCHANGE `score` `exam_score` decimal(5,2) NOT NULL COMMENT \"{score} percent of total\" \n", - "\n", - "Execute? [yes, no]: yes\n", - "Table altered\n" - ] - } - ], + "outputs": [], "source": [ "Exam.alter()" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

exam_date

\n", - " \n", - "
\n", - "

exam_score

\n", - " {score} percent of total\n", - "
1069PHYS21002018FallaNone79.15
1084MATH12502018FallaNone44.69
1088CS31002018Falla2019-04-2317.70
\n", - "

...

\n", - "

Total: 120

\n", - " " - ], - "text/plain": [ - "*student_id *dept *course *term_year *term *section exam_date exam_score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +------------+ +------------+\n", - "1069 PHYS 2100 2018 Fall a None 79.15 \n", - "1084 MATH 1250 2018 Fall a None 44.69 \n", - "1088 CS 3100 2018 Fall a 2019-04-23 17.70 \n", - " ...\n", - " (Total: 120)" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], + "source": [ + "Exam()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's add some grades for today's exam:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "today = datetime.now().date().isoformat()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "today" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# pick 20 random enrollments from the current term\n", + "keys = random.sample(((Enroll - Exam) & CurrentTerm).fetch('KEY'), 20)\n", + "# assign random scores\n", + "for key in keys:\n", + " Exam.insert1(dict(key, score=random.randint(0,10000)/100, exam_date=today))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Exam()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's say we want to rename `score` into `exam_score`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Exam(dj.Manual):\n", + " definition = \"\"\"\n", + " -> Enroll \n", + " ---\n", + " exam_date = null: date \n", + " exam_score : decimal(5,2) # percent of total \n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Say NO!\n", + "Exam.alter()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that rather than renaming, `alter` attempted to drop the old attribute and add the new one. \n", + "\n", + "To rename, we must indicate the old attribute name in curly brackets as the first thing in the attribute comment:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Exam(dj.Manual):\n", + " definition = \"\"\"\n", + " -> Enroll \n", + " ---\n", + " exam_score : decimal(5,2) # percent of total \n", + " exam_date = null: date \n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Exam.alter()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "Exam()" ] @@ -1234,28 +1033,16 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> Enroll\n", - "---\n", - "exam_date=null : date \n", - "exam_score : decimal(5,2) # {score} percent of total\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "Exam.describe();" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1265,48 +1052,34 @@ " -> Enroll \n", " ---\n", " exam_date = null: date \n", - " exam_score : decimal(5,2) # percent of total \n", + " exam_score : decimal(6,3) # percent of total \n", + " photocopy: longb \n", " \"\"\"" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tMODIFY `exam_score` decimal(5,2) NOT NULL COMMENT \"percent of total\" \n", - "\n", - "Execute? [yes, no]: yes\n", - "Table altered\n" - ] - } - ], + "outputs": [], "source": [ "Exam.alter()" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-> Enroll\n", - "---\n", - "exam_date=null : date \n", - "exam_score : decimal(5,2) # percent of total\n", - "\n" - ] - } - ], + "outputs": [], + "source": [ + "Exam()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "Exam.describe();" ] @@ -1322,7 +1095,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1338,154 +1111,18 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tMODIFY `exam_score` tinyint unsigned NOT NULL COMMENT \"percent of total\" \n", - "\n", - "Execute? [yes, no]: yes\n", - "Table altered\n" - ] - } - ], + "outputs": [], "source": [ "Exam.alter()" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

exam_date

\n", - " \n", - "
\n", - "

exam_score

\n", - " percent of total\n", - "
1069PHYS21002018FallaNone79
1084MATH12502018FallaNone45
1088CS31002018Falla2019-04-2318
\n", - "

...

\n", - "

Total: 120

\n", - " " - ], - "text/plain": [ - "*student_id *dept *course *term_year *term *section exam_date exam_score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +------------+ +------------+\n", - "1069 PHYS 2100 2018 Fall a None 79 \n", - "1084 MATH 1250 2018 Fall a None 45 \n", - "1088 CS 3100 2018 Fall a 2019-04-23 18 \n", - " ...\n", - " (Total: 120)" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "Exam()" ] @@ -1499,7 +1136,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1515,48 +1152,16 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tMODIFY `exam_score` decimal(3,2) NOT NULL COMMENT \"percent of total\" \n", - "\n", - "Execute? [yes, no]: yes\n" - ] - }, - { - "ename": "DataError", - "evalue": "(1264, \"Out of range value for column 'exam_score' at row 1\")", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mDataError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mExam\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0malter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36malter\u001b[0;34m(self, prompt, context)\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstore\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mexternal_stores\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mschemas\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdatabase\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexternal\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstore\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 104\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msql\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 105\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mpymysql\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mOperationalError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merror\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;31m# skip if no create privilege\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/dev/datajoint-python/datajoint/connection.py\u001b[0m in \u001b[0;36mquery\u001b[0;34m(self, query, args, as_dict, suppress_warnings, reconnect)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[0;31m# suppress all warnings arising from underlying SQL library\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msimplefilter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ignore\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 148\u001b[0;31m \u001b[0mcur\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 149\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mInterfaceError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mOperationalError\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 150\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mis_connection_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mreconnect\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/cursors.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, query, args)\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0mquery\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmogrify\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 169\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 170\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_query\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 171\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_executed\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mquery\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 172\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/cursors.py\u001b[0m in \u001b[0;36m_query\u001b[0;34m(self, q)\u001b[0m\n\u001b[1;32m 326\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_last_executed\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mq\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 327\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_clear_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 328\u001b[0;31m \u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 329\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_get_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 330\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrowcount\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36mquery\u001b[0;34m(self, sql, unbuffered)\u001b[0m\n\u001b[1;32m 515\u001b[0m \u001b[0msql\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msql\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'surrogateescape'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 516\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute_command\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mCOMMAND\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCOM_QUERY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msql\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 517\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_affected_rows\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_query_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munbuffered\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0munbuffered\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 518\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_affected_rows\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 519\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36m_read_query_result\u001b[0;34m(self, unbuffered)\u001b[0m\n\u001b[1;32m 730\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 731\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMySQLResult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 732\u001b[0;31m \u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 733\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_result\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 734\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserver_status\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36mread\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1073\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1074\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1075\u001b[0;31m \u001b[0mfirst_packet\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_packet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1076\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1077\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfirst_packet\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_ok_packet\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/connections.py\u001b[0m in \u001b[0;36m_read_packet\u001b[0;34m(self, packet_type)\u001b[0m\n\u001b[1;32m 682\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 683\u001b[0m \u001b[0mpacket\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpacket_type\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbuff\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 684\u001b[0;31m \u001b[0mpacket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcheck_error\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 685\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mpacket\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 686\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/protocol.py\u001b[0m in \u001b[0;36mcheck_error\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 218\u001b[0m \u001b[0merrno\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_uint16\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 219\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mDEBUG\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"errno =\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrno\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 220\u001b[0;31m \u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mraise_mysql_exception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 221\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdump\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.6/site-packages/pymysql/err.py\u001b[0m in \u001b[0;36mraise_mysql_exception\u001b[0;34m(data)\u001b[0m\n\u001b[1;32m 107\u001b[0m \u001b[0merrval\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'utf-8'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'replace'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[0merrorclass\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0merror_map\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mInternalError\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 109\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merrorclass\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrno\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrval\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mDataError\u001b[0m: (1264, \"Out of range value for column 'exam_score' at row 1\")" - ] - } - ], + "outputs": [], "source": [ "Exam.alter()" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1571,155 +1176,19 @@ ] }, { - "cell_type": "code", - "execution_count": 32, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tMODIFY `exam_score` decimal(5,2) NOT NULL COMMENT \"percent of total\" \n", - "\n", - "Execute? [yes, no]: yes\n", - "Table altered\n" - ] - } - ], + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "Exam.alter()" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

exam_date

\n", - " \n", - "
\n", - "

exam_score

\n", - " percent of total\n", - "
1069PHYS21002018FallaNone79.00
1084MATH12502018FallaNone45.00
1088CS31002018Falla2019-04-2318.00
\n", - "

...

\n", - "

Total: 120

\n", - " " - ], - "text/plain": [ - "*student_id *dept *course *term_year *term *section exam_date exam_score \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +------------+ +------------+\n", - "1069 PHYS 2100 2018 Fall a None 79.00 \n", - "1084 MATH 1250 2018 Fall a None 45.00 \n", - "1088 CS 3100 2018 Fall a 2019-04-23 18.00 \n", - " ...\n", - " (Total: 120)" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# restored to higher precision but fractional part has been lost:\n", "Exam()" @@ -1734,7 +1203,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -1745,181 +1214,32 @@ " -> Enroll \n", " ---\n", " exam_score : decimal(5,2) # percent of total \n", - " exam_date = null: date \n", " \"\"\"" ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ALTER TABLE `university`.`exam`\n", - "\tMODIFY `exam_score` decimal(5,2) NOT NULL COMMENT \"percent of total\" AFTER `section`,\n", - "\tMODIFY `exam_date` date DEFAULT NULL AFTER `exam_score`,\n", - "\tCOMMENT=\"Exam taken by a student enrolled in a course section and the grade\"\n", - "\n", - "Execute? [yes, no]: yes\n", - "Table altered\n" - ] - } - ], + "outputs": [], "source": [ "Exam.alter()" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " \n", - " Exam taken by a student enrolled in a course section and the grade\n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

dept

\n", - " abbreviated department name, e.g. BIOL\n", - "
\n", - "

course

\n", - " course number, e.g. 1010\n", - "
\n", - "

term_year

\n", - " \n", - "
\n", - "

term

\n", - " \n", - "
\n", - "

section

\n", - " \n", - "
\n", - "

exam_score

\n", - " percent of total\n", - "
\n", - "

exam_date

\n", - " \n", - "
1069PHYS21002018Falla79.00None
1084MATH12502018Falla45.00None
1088CS31002018Falla18.002019-04-23
\n", - "

...

\n", - "

Total: 120

\n", - " " - ], - "text/plain": [ - "*student_id *dept *course *term_year *term *section exam_score exam_date \n", - "+------------+ +------+ +--------+ +-----------+ +------+ +---------+ +------------+ +------------+\n", - "1069 PHYS 2100 2018 Fall a 79.00 None \n", - "1084 MATH 1250 2018 Fall a 45.00 None \n", - "1088 CS 3100 2018 Fall a 18.00 2019-04-23 \n", - " ...\n", - " (Total: 120)" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "Exam()" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "`university`.`exam` (120 tuples)\n", - "Proceed? [yes, No]: yes\n", - "Tables dropped. Restart kernel.\n" - ] - } - ], + "outputs": [], "source": [ "#clean up\n", "Exam.drop()" diff --git a/notebooks/Attachments.ipynb b/notebooks/Attachments.ipynb new file mode 100644 index 0000000..1791d60 --- /dev/null +++ b/notebooks/Attachments.ipynb @@ -0,0 +1,2758 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Attachnments and configurable blobs (new or revised in datajoint 0.12.0)\n", + "This notebooks demonstrates the storaage and retrieval of complex datatypes (blobs) and file attachments in DataJoint.\n", + "\n", + "A **blob** refers to an attribute in a table that can store complex data structures such as numeric arrays.\n", + "\n", + "An **attachment** refers to an attribute that can store an entire file with its filename, etc.\n", + "\n", + "Both blobs and attachments can be stored directly in the tables of the relational database or in configurable external \"stores\" such as network-attached storage servers or object storage systems such [Amazon S3](https://aws.amazon.com/s3/) and [Minio](https://min.io/).\n", + "\n", + "Many of these features existing in prior releases of datajoint but have been substantially expanded in version 0.12.0." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "from IPython import display\n", + "from matplotlib import pyplot as plt\n", + "import os\n", + "import imageio\n", + "import requests\n", + "from ipywidgets import Image\n", + "import ipywidgets\n", + "import numpy as np\n", + "\n", + "import datajoint as dj" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.12.dev7'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.__version__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configure stores\n", + "The following is a configuration defining two external stores. This should only be done once for all users and the configuration file must be saved and provided to all users.\n", + "\n", + "The first store is named `\"shared\"` and is hosted on an S3 endpoint. \n", + "\n", + "The second store is named `\"local\"` and it uses the local path `./dj-store`.\n", + "\n", + "Now these repositories can be used for blobs and attachments." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "## Storage configuration\n", + "\n", + "# set up stores\n", + "dj.config['stores'] = {\n", + " 'shared': dict(\n", + " protocol='s3',\n", + " endpoint='localhost:9000',\n", + " access_key='datajoint',\n", + " secret_key='datajoint',\n", + " bucket='datajoint-demo', \n", + " location=''\n", + " ), \n", + " 'local': { # store in files\n", + " 'protocol': 'file',\n", + " 'location': os.path.abspath('./dj-store')\n", + " }}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dimitri@localhost:3306\n" + ] + } + ], + "source": [ + "# create a schema for this demo\n", + "schema = dj.schema('test_attach')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Proceed to delete entire schema `test_attach`? [yes, No]: yes\n" + ] + } + ], + "source": [ + "schema.drop() # drop if exists to create anew" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# create a schema for this demo\n", + "schema = dj.schema('test_attach')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# A Minimal example of blobs and configurable blobs\n", + "Let's declear the table Test with blobs and attachments stored intrnally and externally." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Test(dj.Manual):\n", + " definition = \"\"\"\n", + " # Test blob and attachments\n", + " id : int\n", + " ---\n", + " b0 : longblob # a python object stored internally in the table\n", + " b1 : blob@shared # a python object stored on S3\n", + " b2 : blob@local # a python object store on the file system\n", + " a0 : attach # a file attachment stored internally in the table\n", + " a1 : attach@shared # a file attachment stored on s3\n", + " a2 : attach@local # a file attachment stored on the file system\n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Create three numpy arrays as save them in different files\n", + "q0, q1, q2 = np.random.randn(3,4), np.random.randn(7), np.random.randn(2, 3, 4)\n", + "f0, f1, f2 = './outfile0.npy', './outfile1.npy', './outfile2.npy'\n", + "np.save(f0, q0)\n", + "np.save(f1, q1)\n", + "np.save(f2, q2)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "About to delete:\n", + "Nothing to delete\n" + ] + } + ], + "source": [ + "Test.delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# insert the blobs and the attachments into the table\n", + "Test.insert1(dict(id=1, b0=q0, b1=q1, b2=q2, a0=f0, a1=f1, a2=f2))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Test blob and attachments\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

id

\n", + " \n", + "
\n", + "

b0

\n", + " a python object stored internally in the table\n", + "
\n", + "

b1

\n", + " a python object stored on S3\n", + "
\n", + "

b2

\n", + " a python object store on the file system\n", + "
\n", + "

a0

\n", + " a file attachment stored internally in the table\n", + "
\n", + "

a1

\n", + " a file attachment stored on s3\n", + "
\n", + "

a2

\n", + " a file attachment stored on the file system\n", + "
1=BLOB==BLOB==BLOB==BLOB==BLOB==BLOB=
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*id b0 b1 b2 a0 a1 a2 \n", + "+----+ +--------+ +--------+ +--------+ +--------+ +--------+ +--------+\n", + "1 =BLOB= =BLOB= =BLOB= =BLOB= =BLOB= =BLOB= \n", + " (Total: 1)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Test()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# delete the attached files\n", + "os.remove(f0)\n", + "os.remove(f1)\n", + "os.remove(f2)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# now fetch them and verify that they retrieved correctly\n", + "result = Test.fetch(as_dict=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.03270063, 1.48132664, -0.47460454, 0.53584166],\n", + " [-0.54839705, 0.06459001, -0.12448809, 0.1326574 ],\n", + " [ 0.68458856, 0.36013697, 0.00544484, 0.30176943]])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result[0]['b0']" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.03270063, 1.48132664, -0.47460454, 0.53584166],\n", + " [-0.54839705, 0.06459001, -0.12448809, 0.1326574 ],\n", + " [ 0.68458856, 0.36013697, 0.00544484, 0.30176943]])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "q0" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array_equal(q0, result[0]['b0'])" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PosixPath('outfile1.npy')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result[0]['a1']" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1.24891915, -1.16716244, -0.55109167, -0.45738901, -0.68241222,\n", + " -0.23278837, 1.23499246])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.load(result[0]['a1'])" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1.24891915, -1.16716244, -0.55109167, -0.45738901, -0.68241222,\n", + " -0.23278837, 1.23499246])" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "q1" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "External file tables for schema `test_attach`:\n", + " \"shared\" s3:\"\n", + " \"local\" file:/home/dimitri/dev/db-programming-with-datajoint/notebooks/dj-store\"" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
1faa3094-d718-075c-9c94-f1a6c0505c55320outfile2.npyNoneNone2019-09-20 20:35:22
784db39e-7622-9539-a896-d3afb854250c237NoneNoneNone2019-09-20 20:35:22
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +------+ +------------+ +----------+ +------------+ +------------+\n", + "1faa3094-d718- 320 outfile2.npy None None 2019-09-20 20:\n", + "784db39e-7622- 237 None None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['local']" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
013ac743-ddf5-afc8-bf7d-40fcf3754758184outfile1.npyNoneNone2019-09-20 20:35:22
90dccd2e-fb2a-02c5-8556-f494b7a4ac5785NoneNoneNone2019-09-20 20:35:22
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +------+ +------------+ +----------+ +------------+ +------------+\n", + "013ac743-ddf5- 184 outfile1.npy None None 2019-09-20 20:\n", + "90dccd2e-fb2a- 85 None None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared']" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(UUID('1faa3094-d718-075c-9c94-f1a6c0505c55'),\n", + " PurePosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/dj-store/test_attach/1f/aa/1faa3094d718075c9c94f1a6c0505c55.outfile2.npy')),\n", + " (UUID('784db39e-7622-9539-a896-d3afb854250c'),\n", + " PurePosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/dj-store/test_attach/78/4d/784db39e76229539a896d3afb854250c'))]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['local'].fetch_external_paths()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(UUID('013ac743-ddf5-afc8-bf7d-40fcf3754758'),\n", + " PurePosixPath('test_attach/01/3a/013ac743ddf5afc8bf7d40fcf3754758.outfile1.npy')),\n", + " (UUID('90dccd2e-fb2a-02c5-8556-f494b7a4ac57'),\n", + " PurePosixPath('test_attach/90/dc/90dccd2efb2a02c58556f494b7a4ac57'))]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].fetch_external_paths()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
013ac743-ddf5-afc8-bf7d-40fcf3754758184outfile1.npyNoneNone2019-09-20 20:35:22
90dccd2e-fb2a-02c5-8556-f494b7a4ac5785NoneNoneNone2019-09-20 20:35:22
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +------+ +------------+ +----------+ +------------+ +------------+\n", + "013ac743-ddf5- 184 outfile1.npy None None 2019-09-20 20:\n", + "90dccd2e-fb2a- 85 None None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].used()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
1faa3094-d718-075c-9c94-f1a6c0505c55320outfile2.npyNoneNone2019-09-20 20:35:22
784db39e-7622-9539-a896-d3afb854250c237NoneNoneNone2019-09-20 20:35:22
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +------+ +------------+ +----------+ +------------+ +------------+\n", + "1faa3094-d718- 320 outfile2.npy None None 2019-09-20 20:\n", + "784db39e-7622- 237 None None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['local'].used()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------+ +------+ +------------+ +----------+ +------------+ +-----------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].unused()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "About to delete:\n", + "`test_attach`.`test`: 1 items\n", + "Proceed? [yes, No]: yes\n", + "Committed.\n" + ] + } + ], + "source": [ + "Test.delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
013ac743-ddf5-afc8-bf7d-40fcf3754758184outfile1.npyNoneNone2019-09-20 20:35:22
90dccd2e-fb2a-02c5-8556-f494b7a4ac5785NoneNoneNone2019-09-20 20:35:22
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +------+ +------------+ +----------+ +------------+ +------------+\n", + "013ac743-ddf5- 184 outfile1.npy None None 2019-09-20 20:\n", + "90dccd2e-fb2a- 85 None None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared']" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 2/2 [00:00<00:00, 74.11it/s]\n" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].delete() # deleted" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "0it [00:00, ?it/s]\n", + "100%|██████████| 2/2 [00:00<00:00, 70.79it/s]\n" + ] + } + ], + "source": [ + "# cleanup\n", + "for s in schema.external.values():\n", + " s.delete()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Lookup of images on the web\n", + "We create a lookup table, WebImage to point to some images available on the web" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class WebImage(dj.Lookup):\n", + " definition = \"\"\"\n", + " # A reference to a web image\n", + " image_number : int\n", + " ---\n", + " image_name : varchar(30)\n", + " image_description : varchar(1024)\n", + " image_url : varchar(1024)\n", + " \n", + " unique index(image_name)\n", + " \"\"\"\n", + " contents = [\n", + " (0, \"pyramidal\", \n", + " \n", + " 'Coronal section containing the chronically imaged pyramidal neuron \"dow\" '\\\n", + " '(visualized by green GFP) does not stain for GABA (visualized by antibody staining in red). '\\\n", + " 'Confocal image stack, overlay of GFP and GABA channels. Scale bar: 100 um',\n", + " \n", + " \"https://upload.wikimedia.org/wikipedia/commons/d/dc/PLoSBiol4.e126.Fig6fNeuron.jpg\"\n", + " ),\n", + " (1, \"striatal\", \n", + " \n", + " \"Mouse spiny striatal projection neuron expressing a transgenic fluorescent protein \"\\\n", + " \"(colored yellow) delivered by a recombinant virus (AAV). \"\\\n", + " \"The striatal interneuron are stainerd in green for the neurokinin-1 receptor.\",\n", + " \n", + " \"https://upload.wikimedia.org/wikipedia/commons/e/e8/Striatal_neuron_in_an_interneuron_cage.jpg\"\n", + " )\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Preview the images directly from the web" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5924873873724ae1b142b8776b46bbd0", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'https://upload.wikimedia.org/wikipedia/commons/d/dc/PLoSBiol4.e126.Fig6fNeuron.jpg', format='url…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Image.from_url((WebImage & 'image_number=0').fetch1('image_url'))" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "530a6e0f4ad041528d738808d5642088", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'https://upload.wikimedia.org/wikipedia/commons/e/e8/Striatal_neuron_in_an_interneuron_cage.jpg',…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Image.from_url((WebImage & 'image_number=1').fetch1('image_url'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define a table with attachments\n", + "Now we can use the stores to define attachment attributes in the form `attribute_name : attach@store # comment` where the store is either `@local` or `@shared` as defined above.\n", + "\n", + "Let's define the table `OriginalFile` to automatically download and attach files from `WebImage` and stores the attachments in the shared store." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class OriginalFile(dj.Imported):\n", + " definition = \"\"\"\n", + " -> WebImage\n", + " ---\n", + " image_file : attach@shared\n", + " \"\"\"\n", + " \n", + " def make(self, key):\n", + " # get the URL\n", + " url = (WebImage & key).fetch1('image_url')\n", + " \n", + " # download the file from the web\n", + " local_file = os.path.join(os.path.abspath('.'), url.split('/')[-1])\n", + " with open(local_file, 'wb') as f:\n", + " f.write(requests.get(url).content)\n", + " \n", + " # attach the file\n", + " self.insert1(dict(key, image_file=local_file))\n", + " \n", + " # delete the downloaded file\n", + " os.remove(local_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "WebImage\n", + "\n", + "\n", + "WebImage\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OriginalFile\n", + "\n", + "\n", + "OriginalFile\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "WebImage->OriginalFile\n", + "\n", + "\n", + "\n", + "\n", + "Test\n", + "\n", + "\n", + "Test\n", + "\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(schema)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [], + "source": [ + "# perform the download\n", + "OriginalFile.populate()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + "

image_number

\n", + " \n", + "
\n", + "

image_file

\n", + " \n", + "
0=BLOB=
1=BLOB=
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*image_number image_file\n", + "+------------+ +--------+\n", + "0 =BLOB= \n", + "1 =BLOB= \n", + " (Total: 2)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "OriginalFile()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9b85398bbd984658ae143dee9ad6d0ca", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'\\xff\\xd8\\xff\\xe0\\x00\\x10JFIF\\x00\\x01\\x01\\x01\\x00`\\x00`\\x00\\x00\\xff\\xdb\\x00C\\x00\\x01\\x01\\x01\\x01\\…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# preview downloaded attachment\n", + "file = (OriginalFile & 'image_number=1').fetch1('image_file')\n", + "Image.from_file(file)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "os.remove(file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Extract images into blobs\n", + "Now let's define another class that extracts imags from attached files and stores as blobs in the local store." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "# Declare a table with a configurable blob\n", + "@schema\n", + "class Slide(dj.Computed):\n", + " definition = \"\"\"\n", + " -> OriginalFile\n", + " ---\n", + " image_array : blob@local # array in specified store\n", + " \"\"\"\n", + " \n", + " def make(self, key):\n", + " # get the attached file\n", + " file = (OriginalFile & key).fetch1('image_file')\n", + " \n", + " # save image data\n", + " self.insert1(dict(key, image_array=imageio.imread(file)))\n", + " \n", + " # remove the downloaded file\n", + " os.remove(file)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "Slide.populate()" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + "

image_number

\n", + " \n", + "
\n", + "

image_array

\n", + " array in specified store\n", + "
1=BLOB=
0=BLOB=
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*image_number image_arra\n", + "+------------+ +--------+\n", + "1 =BLOB= \n", + "0 =BLOB= \n", + " (Total: 2)" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Slide()" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "External file tables for schema `test_attach`:\n", + " \"shared\" s3:\"\n", + " \"local\" file:/home/dimitri/dev/db-programming-with-datajoint/notebooks/dj-store\"" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
403b8717-6c83-fa52-d381-e1371e61e4ac1324311NoneNoneNone2019-09-20 20:35:59
cbc9f232-6abd-6863-c449-9cb82a11e47a1707130NoneNoneNone2019-09-20 20:35:59
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +---------+ +------------+ +----------+ +------------+ +------------+\n", + "403b8717-6c83- 1324311 None None None 2019-09-20 20:\n", + "cbc9f232-6abd- 1707130 None None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['local']" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Plot image form a blob\n", + "plt.imshow((Slide & 'image_number=0').fetch1('image_array'));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Caching\n", + "By default, the data from blobs and attachments are retrieved from remote stores with every fetch command. \n", + "For repeated queries, a cache folder may be specified to improve performance and reduce cost of operations.\n", + "After the first fetch of a given blob or attachment, it will be read from the cache. " + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "# configure the cache\n", + "dj.config['cache'] = './dj-cache'" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [], + "source": [ + "# clear the cache for the timing test\n", + "import shutil\n", + "if os.path.isdir(dj.config['cache']):\n", + " shutil.rmtree(dj.config['cache'])" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "17.7 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n1 -r1\n", + "\n", + "# first time no cache\n", + "files = OriginalFile.fetch('image_file')" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11.4 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n1 -r1\n", + "\n", + "# now with cache\n", + "files = OriginalFile.fetch('image_file')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deleting\n", + "Deleting from tables using external storage is just as simple and transaction-safe as with all other kinds of attributes. Simply use the `delete` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "External file tables for schema `test_attach`:\n", + " \"shared\" s3:\"\n", + " \"local\" file:/home/dimitri/dev/db-programming-with-datajoint/notebooks/dj-store\"" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
00bffedc-d5c2-f928-f0d5-4d31fe01311d806133PLoSBiol4.e126.Fig6fNeuron.jpgNoneNone2019-09-20 20:35:53
81c1c2d3-3c08-66ed-1d64-c96376539195895051Striatal_neuron_in_an_interneuron_cage.jpgNoneNone2019-09-20 20:35:53
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +--------+ +------------+ +----------+ +------------+ +------------+\n", + "00bffedc-d5c2- 806133 PLoSBiol4.e126 None None 2019-09-20 20:\n", + "81c1c2d3-3c08- 895051 Striatal_neuro None None 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared']" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------+ +------+ +------------+ +----------+ +------------+ +-----------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].unused()" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "About to delete:\n", + "`test_attach`.`__slide`: 1 items\n", + "`test_attach`.`_original_file`: 1 items\n", + "`test_attach`.`#web_image`: 1 items\n", + "Proceed? [yes, No]: yes\n", + "Committed.\n" + ] + } + ], + "source": [ + "(WebImage & 'image_number=0').delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
00bffedc-d5c2-f928-f0d5-4d31fe01311d806133PLoSBiol4.e126.Fig6fNeuron.jpgNoneNone2019-09-20 20:35:53
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +--------+ +------------+ +----------+ +------------+ +------------+\n", + "00bffedc-d5c2- 806133 PLoSBiol4.e126 None None 2019-09-20 20:\n", + " (Total: 1)" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].unused()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Deleting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For the sake of performance, deleting from the data tables does not remove the data from external storage. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `delete` method of the external table deletes its **unused** entries and their corresponding external files." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "External file tables for schema `test_attach`:\n", + " \"shared\" s3:\"\n", + " \"local\" file:/home/dimitri/dev/db-programming-with-datajoint/notebooks/dj-store\"" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You may cleanup the external table using its `delete` method. It is a transaction-safe operation and can be performed at any time." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 1/1 [00:00<00:00, 47.74it/s]\n" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['local'].delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 1/1 [00:00<00:00, 66.55it/s]\n" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "0it [00:00, ?it/s]\n", + "0it [00:00, ?it/s]\n" + ] + } + ], + "source": [ + "for s in schema.external.values():\n", + " s.delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
81c1c2d3-3c08-66ed-1d64-c96376539195895051Striatal_neuron_in_an_interneuron_cage.jpgNoneNone2019-09-20 20:35:53
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +--------+ +------------+ +----------+ +------------+ +------------+\n", + "81c1c2d3-3c08- 895051 Striatal_neuro None None 2019-09-20 20:\n", + " (Total: 1)" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].used()" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------+ +------+ +------------+ +----------+ +------------+ +-----------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['shared'].unused()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/External-Migration.ipynb b/notebooks/External-Migration.ipynb new file mode 100644 index 0000000..032b5e2 --- /dev/null +++ b/notebooks/External-Migration.ipynb @@ -0,0 +1,942 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Migration of External Storage from DataJoint 0.11.* to 0.12.*\n", + "DataJoint 0.12 improves the efficiency of blob storage and expands its capabilities. Unfortunately, this breaks backward compatibility for external storage used in previous versions. This notebook describes the migration procedure from a 0.11 external store.\n", + "\n", + "First, let's emulate an legacy table with external storage. You do not need to perform these steps if you are migrating an existing database. I `git`-cloned `datajoint-python` as a subfolder in the datajoint folder, checked out the legacy version `v0.11.1`, and renamed the subfolder into `dj011`. This allows me to import the legacy version of datajoint while keeping the current version available as well:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import dj011 as dj # legacy version of datajoint" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.11.1'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "dj.config['database.password'] = 'datajoint'\n", + "dj.config['database.user'] = 'datajoint'\n", + "dj.config['database.host'] = 'localhost'" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting datajoint@localhost:3306\n", + "Proceed to delete entire schema `djtest_blobs`? [yes, No]: yes\n" + ] + } + ], + "source": [ + "schema = dj.schema('djtest_blobs')\n", + "schema.drop()\n", + "schema = dj.schema('djtest_blobs')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Configure stores\n", + "import os \n", + "\n", + "dj.config['external'] = dict(\n", + " protocol='s3',\n", + " endpoint=\"localhost:9000\",\n", + " bucket='migrate-test',\n", + " location='store',\n", + " access_key=\"datajoint\",\n", + " secret_key=\"datajoint\")\n", + "\n", + "dj.config['external-shared'] = dict(\n", + " protocol='s3',\n", + " endpoint=\"localhost:9000\",\n", + " bucket='migrate-test',\n", + " location='maps',\n", + " access_key=\"datajoint\",\n", + " secret_key=\"datajoint\")\n", + "\n", + "dj.config['external-local'] = dict(\n", + " protocol='file',\n", + " location=os.path.expanduser('~/temp/migrate-test'),\n", + " access_key=\"datajoint\",\n", + " secret_key=\"datajoint\")\n", + "\n", + "\n", + "dj.config['cache'] = os.path.expanduser('~/temp/dj-cache')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's define the legacy-style table with external blobs:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class A(dj.Manual):\n", + " definition = \"\"\"\n", + " id : int \n", + " ---\n", + " blob_external : external # uses S3\n", + " blob_share : external-shared # uses S3\n", + " \"\"\"\n", + "\n", + "@schema\n", + "class B(dj.Manual):\n", + " definition = \"\"\"\n", + " id : int \n", + " ---\n", + " blob_local : external-local # uses files\n", + " blob_share : external-shared # uses S3\n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "A.insert((\n", + " (0, np.random.randn(2,3,4), np.random.randn(3)),\n", + " (1, np.array([1,2,3]), np.array([1,2]))\n", + "))\n", + "\n", + "B.insert((\n", + " (0, np.random.randn(2,3,4), np.random.randn(3)),\n", + " (1, np.array([1,2,3]), np.array([1,2]))\n", + "))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Upgrade legacy blobs\n", + "(restart kernel)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.12.dev4'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "dj.config['database.password'] = 'datajoint'\n", + "dj.config['database.user'] = 'datajoint'\n", + "dj.config['database.host'] = 'localhost'" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting datajoint@localhost:3306\n" + ] + } + ], + "source": [ + "schema = dj.schema('djtest_blobs')\n", + "query = schema.connection.query" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Configure stores\n", + "import os \n", + "\n", + "default_store = 'external' # naming the unnamed external store\n", + "\n", + "dj.config['stores'] = {\n", + " \n", + " default_store: dict(\n", + " protocol='s3',\n", + " endpoint=\"localhost:9000\",\n", + " bucket='migrate-test',\n", + " location='store',\n", + " access_key=\"datajoint\",\n", + " secret_key=\"datajoint\"),\n", + " \n", + " 'shared': dict(\n", + " protocol='s3',\n", + " endpoint=\"localhost:9000\",\n", + " bucket='migrate-test',\n", + " location='maps',\n", + " access_key=\"datajoint\",\n", + " secret_key=\"datajoint\"),\n", + " \n", + " 'local': dict(\n", + " protocol='file',\n", + " location=os.path.expanduser('~/temp/migrate-test'))\n", + "}\n", + "\n", + "dj.config['cache'] = os.path.expanduser('~/temp/dj-cache')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class C(dj.Manual):\n", + " definition = \"\"\"\n", + " id : int\n", + " ---\n", + " blo : blob@shared # just a check\n", + " \"\"\"\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "id : int \n", + "---\n", + "blo : blob@shared # just a check\n", + "INDEX (blo)\n", + "\n" + ] + } + ], + "source": [ + "C.describe();" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "schema.spawn_missing_classes()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "DataJointError", + "evalue": "Legacy datatype `external`.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDataJointError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m~/.local/lib/python3.6/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 700\u001b[0m \u001b[0mtype_pprinters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype_printers\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 701\u001b[0m deferred_pprinters=self.deferred_printers)\n\u001b[0;32m--> 702\u001b[0;31m \u001b[0mprinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpretty\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 703\u001b[0m \u001b[0mprinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 704\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mstream\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetvalue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.6/site-packages/IPython/lib/pretty.py\u001b[0m in \u001b[0;36mpretty\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 400\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 401\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__dict__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'__repr__'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 402\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_repr_pprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 403\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 404\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0m_default_pprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.6/site-packages/IPython/lib/pretty.py\u001b[0m in \u001b[0;36m_repr_pprint\u001b[0;34m(obj, p, cycle)\u001b[0m\n\u001b[1;32m 695\u001b[0m \u001b[0;34m\"\"\"A pprint that just redirects to the normal repr function.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 696\u001b[0m \u001b[0;31m# Find newlines and replace them with p.break_()\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 697\u001b[0;31m \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrepr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 698\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0moutput_line\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplitlines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 699\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/expression.py\u001b[0m in \u001b[0;36m__repr__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 393\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 394\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 395\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'loglevel'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'debug'\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpreview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 396\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 397\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mpreview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlimit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/expression.py\u001b[0m in \u001b[0;36mpreview\u001b[0;34m(self, limit, width)\u001b[0m\n\u001b[1;32m 399\u001b[0m \u001b[0mreturns\u001b[0m \u001b[0ma\u001b[0m \u001b[0mpreview\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mcontents\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mquery\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 400\u001b[0m \"\"\"\n\u001b[0;32m--> 401\u001b[0;31m \u001b[0mheading\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mheading\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 402\u001b[0m \u001b[0mrel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mproj\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mheading\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnon_blobs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 403\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlimit\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36mheading\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 52\u001b[0m 'Missing schema decorator on the class? (e.g. @schema)')\n\u001b[1;32m 53\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 54\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_heading\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minit_from_database\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdatabase\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtable_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 55\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_heading\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 56\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/heading.py\u001b[0m in \u001b[0;36minit_from_database\u001b[0;34m(self, conn, database, table_name)\u001b[0m\n\u001b[1;32m 227\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mStopIteration\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 228\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'type'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstartswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'external'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 229\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Legacy datatype `{type}`.'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mattr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 230\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Unknown attribute type `{type}`'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mattr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m attr.update(\n", + "\u001b[0;31mDataJointError\u001b[0m: Legacy datatype `external`." + ] + }, + { + "ename": "DataJointError", + "evalue": "Legacy datatype `external`.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDataJointError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m~/.local/lib/python3.6/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 343\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_real_method\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_method\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 344\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmethod\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 345\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 346\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 347\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/expression.py\u001b[0m in \u001b[0;36m_repr_html_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 421\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 422\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_repr_html_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 423\u001b[0;31m \u001b[0mheading\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mheading\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 424\u001b[0m \u001b[0mrel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mproj\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mheading\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnon_blobs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 425\u001b[0m \u001b[0minfo\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mheading\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtable_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36mheading\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 52\u001b[0m 'Missing schema decorator on the class? (e.g. @schema)')\n\u001b[1;32m 53\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 54\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_heading\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minit_from_database\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdatabase\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtable_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 55\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_heading\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 56\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/heading.py\u001b[0m in \u001b[0;36minit_from_database\u001b[0;34m(self, conn, database, table_name)\u001b[0m\n\u001b[1;32m 227\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mStopIteration\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 228\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'type'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstartswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'external'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 229\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Legacy datatype `{type}`.'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mattr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 230\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Unknown attribute type `{type}`'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mattr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m attr.update(\n", + "\u001b[0;31mDataJointError\u001b[0m: Legacy datatype `external`." + ] + } + ], + "source": [ + "A()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "LEGACY_HASH_SIZE = 43\n", + "\n", + "legacy_external = dj.FreeTable(\n", + " schema.connection,\n", + " '`{db}`.`~external`'.format(db=schema.database))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " the hash of stored object + store name\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
AbOdLZWSNmvYrfESJci85yLPplWIUM9E7UmyzHg0ApM2372019-07-19 13:22:54
BEq9eh9LlqkPOKS8PwbqsOX3PTom0MhLmqvlN43yfrsshared532019-07-19 13:22:54
FoRROa2LWM6_wx0RIQ0J-LVvgm256cqDQfJa066HoTEshared372019-07-19 13:22:54
l3MDivFfPe1GV74Fdyky4YSLVKq3Y4x_U7mtzAReSaUlocal2372019-07-19 13:22:54
U7u2I13bb6Zx2bC7yn_J8yCYos6fDebQBJhaUf4ho2Eshared532019-07-19 13:22:54
_Fhi2GUBB0fgxcSP2q-isgncIUTdgGK7ivHiySAU_94402019-07-19 13:22:54
_Fhi2GUBB0fgxcSP2q-isgncIUTdgGK7ivHiySAU_94local402019-07-19 13:22:54
\n", + " \n", + "

Total: 7

\n", + " " + ], + "text/plain": [ + "FreeTable(`djtest_blobs`.`~external`)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "legacy_external" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "# get referencing tables\n", + "refs = query(\"\"\"\n", + "SELECT concat('`', table_schema, '`.`', table_name, '`') as referencing_table, column_name, constraint_name\n", + "FROM information_schema.key_column_usage\n", + "WHERE referenced_table_name=\"{tab}\" and referenced_table_schema=\"{db}\"\n", + "\"\"\".format(tab=legacy_external.table_name, db=legacy_external.database), as_dict=True).fetchall()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "for ref in refs:\n", + " # get comment\n", + " column = query(\n", + " 'SHOW FULL COLUMNS FROM {referencing_table}'\n", + " 'WHERE Field=\"{column_name}\"'.format(**ref), as_dict=True).fetchone()\n", + "\n", + " store, comment = re.match(\n", + " r':external(-(?P.+))?:(?P.*)', \n", + " column['Comment']).group('store', 'comment')\n", + "\n", + " # get all the hashes from the reference\n", + " hashes = {x[0] for x in query(\n", + " 'SELECT `{column_name}` FROM {referencing_table}'.format(**ref))}\n", + "\n", + " # sanity check make sure that store suffixes match\n", + " if store is None:\n", + " assert all(len(_) == LEGACY_HASH_SIZE for _ in hashes)\n", + " else:\n", + " assert all(_[LEGACY_HASH_SIZE:] == store for _ in hashes)\n", + "\n", + " # create new-style external table\n", + " ext = schema.external[store or default_store]\n", + "\n", + " # add the new-style reference field\n", + " temp_suffix = 'tempsub'\n", + "\n", + " try:\n", + " query(\"\"\"ALTER TABLE {referencing_table} \n", + " ADD COLUMN `{column_name}_{temp_suffix}` {type} DEFAULT NULL\n", + " COMMENT \":blob@{store}:{comment}\"\n", + " \"\"\".format(type=dj.declare.UUID_DATA_TYPE, \n", + " temp_suffix=temp_suffix, \n", + " store=(store or default_store), comment=comment, **ref))\n", + " except:\n", + " print('Column already added')\n", + " pass\n", + "\n", + "\n", + " # Copy references into the new external table\n", + " # No Windows! Backslashes will cause problems\n", + "\n", + " contents_hash_function = {\n", + " 'file': lambda ext, relative_path: dj.hash.uuid_from_file(os.path.join(ext.spec['location'], relative_path)),\n", + " 's3': lambda ext, relative_path: dj.hash.uuid_from_buffer(ext.s3.get(relative_path))\n", + " }\n", + "\n", + " for _hash, size in zip(*legacy_external.fetch('hash', 'size')):\n", + " if _hash in hashes:\n", + " relative_path = os.path.join(schema.database, _hash)\n", + " uuid = dj.hash.uuid_from_buffer(init_string=relative_path)\n", + " ext.insert1(dict(\n", + " filepath=relative_path,\n", + " size=size,\n", + " contents_hash=contents_hash_function[ext.spec['protocol']](ext, relative_path),\n", + " hash=uuid\n", + " ), skip_duplicates=True)\n", + "\n", + " query('UPDATE {referencing_table} '\n", + " 'SET `{column_name}_{temp_suffix}`=%s '\n", + " 'WHERE `{column_name}` = \"{_hash}\"'\n", + " .format(_hash=_hash, temp_suffix=temp_suffix, **ref), uuid.bytes)\n", + "\n", + " # check that all have been copied\n", + " check = query('SELECT * FROM {referencing_table} '\n", + " 'WHERE `{column_name}` IS NOT NULL'\n", + " ' AND `{column_name}_{temp_suffix}` IS NULL'\n", + " .format(temp_suffix=temp_suffix, **ref)).fetchall()\n", + "\n", + " assert len(check) == 0, 'Some hashes havent been migrated'\n", + "\n", + " # drop old foreign key, rename, and create new foreign key\n", + " query(\"\"\"\n", + " ALTER TABLE {referencing_table}\n", + " DROP FOREIGN KEY `{constraint_name}`,\n", + " DROP COLUMN `{column_name}`,\n", + " CHANGE COLUMN `{column_name}_{temp_suffix}` `{column_name}` {type} DEFAULT NULL\n", + " COMMENT \":blob@{store}:{comment}\",\n", + " ADD FOREIGN KEY (`{column_name}`) REFERENCES {ext_table_name} (`hash`)\n", + " \"\"\".format(temp_suffix=temp_suffix, \n", + " ext_table_name=ext.full_table_name, \n", + " type=dj.declare.UUID_DATA_TYPE, \n", + " store=(store or default_store), comment=comment, **ref))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " the hash of stored object + store name\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
AbOdLZWSNmvYrfESJci85yLPplWIUM9E7UmyzHg0ApM2372019-07-19 13:22:54
BEq9eh9LlqkPOKS8PwbqsOX3PTom0MhLmqvlN43yfrsshared532019-07-19 13:22:54
FoRROa2LWM6_wx0RIQ0J-LVvgm256cqDQfJa066HoTEshared372019-07-19 13:22:54
l3MDivFfPe1GV74Fdyky4YSLVKq3Y4x_U7mtzAReSaUlocal2372019-07-19 13:22:54
U7u2I13bb6Zx2bC7yn_J8yCYos6fDebQBJhaUf4ho2Eshared532019-07-19 13:22:54
_Fhi2GUBB0fgxcSP2q-isgncIUTdgGK7ivHiySAU_94402019-07-19 13:22:54
_Fhi2GUBB0fgxcSP2q-isgncIUTdgGK7ivHiySAU_94local402019-07-19 13:22:54
\n", + " \n", + "

Total: 7

\n", + " " + ], + "text/plain": [ + "FreeTable(`djtest_blobs`.`~external`)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "legacy_external" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "# Drop the old external table but make sure it's no longer referenced\n", + "# get referencing tables\n", + "refs = query(\"\"\"\n", + "SELECT concat('`', table_schema, '`.`', table_name, '`') as referencing_table, column_name, constraint_name\n", + "FROM information_schema.key_column_usage\n", + "WHERE referenced_table_name=\"{tab}\" and referenced_table_schema=\"{db}\"\n", + "\"\"\".format(tab=legacy_external.table_name, db=legacy_external.database), as_dict=True).fetchall()\n", + "\n", + "assert not refs, 'Some references still exist'" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "# drop old external table\n", + "legacy_external.drop_quick()" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

filepath

\n", + " relative filepath used in the filepath datatype\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
83186db4-c969-0298-3ec8-1da403c715d540djtest_blobs/_Fhi2GUBB0fgxcSP2q-isgncIUTdgGK7ivHiySAU_94069fe219-23b4-62e5-fbfe-da572fc47f7d2019-07-19 13:34:30
b052bba1-018d-ec41-3c35-33bb7df4cb90237NoneNone2019-07-19 13:41:29
d4b9b701-b577-d088-e6f7-135f060b9cfb237djtest_blobs/AbOdLZWSNmvYrfESJci85yLPplWIUM9E7UmyzHg0ApM69391462-c54e-1f19-2f78-7f3f2eccb9b92019-07-19 13:34:30
ea5894dc-3a46-5ebe-4bbd-e80ebf08b16153NoneNone2019-07-19 13:41:29
\n", + " \n", + "

Total: 4

\n", + " " + ], + "text/plain": [ + "*hash size filepath contents_hash timestamp \n", + "+------------+ +------+ +------------+ +------------+ +------------+\n", + "83186db4-c969- 40 djtest_blobs/_ 069fe219-23b4- 2019-07-19 13:\n", + "b052bba1-018d- 237 None None 2019-07-19 13:\n", + "d4b9b701-b577- 237 djtest_blobs/A 69391462-c54e- 2019-07-19 13:\n", + "ea5894dc-3a46- 53 None None 2019-07-19 13:\n", + " (Total: 4)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['external']" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "\n", + "dj.config['database.password'] = 'datajoint'\n", + "dj.config['database.user'] = 'datajoint'\n", + "dj.config['database.host'] = 'localhost'\n", + "\n", + "schema = dj.schema('djtest_blobs')\n", + "query = schema.connection.query" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "schema.spawn_missing_classes()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dj.ERD(schema)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "A.insert((\n", + " (2, np.random.randn(2,3,4), np.random.randn(3)),\n", + " (3, np.array([1,2,3]), np.array([1,2]))\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "A.fetch('blob_share')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "os.path.join('one', 'two', 'three\\\\four')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Filepaths.ipynb b/notebooks/Filepaths.ipynb new file mode 100644 index 0000000..e8d20e9 --- /dev/null +++ b/notebooks/Filepaths.ipynb @@ -0,0 +1,1926 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython import display\n", + "from ipywidgets import Image\n", + "\n", + "import os\n", + "import datajoint as dj" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dimitri@localhost:3306\n" + ] + } + ], + "source": [ + "schema = dj.schema('dimitri_filepath')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Proceed to delete entire schema `dimitri_filepath`? [yes, No]: yes\n" + ] + } + ], + "source": [ + "schema.drop() # drop schema to start demo from scratch" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "schema = dj.schema('dimitri_filepath')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "## Storage configuration\n", + "\n", + "# set up stores\n", + "dj.config['stores'] = {\n", + " 'remote': dict( # store in minio\n", + " stage=os.path.abspath('./stage'),\n", + " protocol='s3',\n", + " endpoint='localhost:9000',\n", + " access_key='datajoint',\n", + " secret_key='datajoint',\n", + " bucket='datajoint-demo', \n", + " location='code-clinic'), \n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Download some images off the web into ./stage" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# Step 1: Find a bunch of images on the web\n", + "logos = dict(\n", + " ucsd='https://upload.wikimedia.org/wikipedia/commons/f/f6/UCSD_logo.png',\n", + " datajoint='https://datajoint.io/static/images/DJiotitle.png',\n", + " utah='https://umc.utah.edu/wp-content/uploads/sites/15/2015/01/Ulogo_400p.png',\n", + " bcm='https://upload.wikimedia.org/wikipedia/commons/5/5d/Baylor_College_of_Medicine_Logo.png',\n", + " pydata='https://pydata.org/wp-content/uploads/2018/10/pydata-logo.png',\n", + " python='https://www.python.org/static/community_logos/python-logo-master-v3-TM.png',\n", + " pni='https://vathes.com/2018/05/24/Princeton-Neuroscience-Institute-Partners-with-Vathes-to-Support-the-Adoption-of-DataJoint/PNI%20logo.png')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "a7a51c63e3bb4d5d96ebb26f02c5c984", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'https://www.python.org/static/community_logos/python-logo-master-v3-TM.png', format='url')" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Image.from_url(logos['python'])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Organization(dj.Lookup):\n", + " definition = \"\"\"\n", + " organization : varchar(30)\n", + " --- \n", + " logo_url : varchar(255)\n", + " \"\"\"\n", + " contents = logos.items()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

organization

\n", + " \n", + "
\n", + "

logo_url

\n", + " \n", + "
bcmhttps://upload.wikimedia.org/wikipedia/commons/5/5d/Baylor_College_of_Medicine_Logo.png
datajointhttps://datajoint.io/static/images/DJiotitle.png
pnihttps://vathes.com/2018/05/24/Princeton-Neuroscience-Institute-Partners-with-Vathes-to-Support-the-Adoption-of-DataJoint/PNI%20logo.png
pydatahttps://pydata.org/wp-content/uploads/2018/10/pydata-logo.png
pythonhttps://www.python.org/static/community_logos/python-logo-master-v3-TM.png
ucsdhttps://upload.wikimedia.org/wikipedia/commons/f/f6/UCSD_logo.png
utahhttps://umc.utah.edu/wp-content/uploads/sites/15/2015/01/Ulogo_400p.png
\n", + " \n", + "

Total: 7

\n", + " " + ], + "text/plain": [ + "*organization logo_url \n", + "+------------+ +------------+\n", + "bcm https://upload\n", + "datajoint https://datajo\n", + "pni https://vathes\n", + "pydata https://pydata\n", + "python https://www.py\n", + "ucsd https://upload\n", + "utah https://umc.ut\n", + " (Total: 7)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Organization()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "import requests \n", + "\n", + "@schema\n", + "class Logo(dj.Imported):\n", + " definition = \"\"\"\n", + " -> Organization\n", + " ---\n", + " logo_image : filepath@remote\n", + " \"\"\"\n", + " \n", + " path = os.path.join(dj.config['stores']['remote']['stage'], 'organizations', 'logos')\n", + " \n", + " def make(self, key):\n", + " # create the subfolder and download the logo into local_file \n", + " os.makedirs(self.path, exist_ok=True)\n", + " url = (Organization & key).fetch1('logo_url')\n", + " local_file = os.path.join(self.path, key['organization'] + os.path.splitext(url)[1])\n", + " print(local_file)\n", + " with open(local_file, 'wb') as f:\n", + " f.write(requests.get(url).content)\n", + " # sync up\n", + " self.insert1(dict(key, logo_image=local_file)) " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/bcm.png\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/datajoint.png\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/pni.png\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/pydata.png\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/python.png\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/ucsd.png\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/utah.png\n" + ] + } + ], + "source": [ + "Logo.populate()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

organization

\n", + " \n", + "
\n", + "

logo_image

\n", + " \n", + "
pydata=BLOB=
utah=BLOB=
datajoint=BLOB=
ucsd=BLOB=
pni=BLOB=
python=BLOB=
bcm=BLOB=
\n", + " \n", + "

Total: 7

\n", + " " + ], + "text/plain": [ + "*organization logo_image\n", + "+------------+ +--------+\n", + "pydata =BLOB= \n", + "utah =BLOB= \n", + "datajoint =BLOB= \n", + "ucsd =BLOB= \n", + "pni =BLOB= \n", + "python =BLOB= \n", + "bcm =BLOB= \n", + " (Total: 7)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Logo()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'organization': 'pydata',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/pydata.png')},\n", + " {'organization': 'utah',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/utah.png')},\n", + " {'organization': 'datajoint',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/datajoint.png')},\n", + " {'organization': 'ucsd',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/ucsd.png')},\n", + " {'organization': 'pni',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/pni.png')},\n", + " {'organization': 'python',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/python.png')},\n", + " {'organization': 'bcm',\n", + " 'logo_image': PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/bcm.png')}]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Logo.fetch(as_dict=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# delete the local repository completely\n", + "import shutil\n", + "shutil.rmtree(dj.config['stores']['remote']['stage'])" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "paths = Logo().fetch('logo_image')" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/pydata.png'),\n", + " PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/utah.png'),\n", + " PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/datajoint.png'),\n", + " PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/ucsd.png'),\n", + " PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/pni.png'),\n", + " PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/python.png'),\n", + " PosixPath('/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/organizations/logos/bcm.png')],\n", + " dtype=object)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "paths" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5cea364859fc47c79ca2f1e79dfb2f2c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x00\\xfb\\x00\\x00\\x00\\xc9\\x08\\x03\\x00\\x00\\x00\\xd2\\x1e\\…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Image.from_file(paths[4])" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "About to delete:\n", + "`dimitri_filepath`.`_logo`: 2 items\n", + "Proceed? [yes, No]: yes\n", + "Committed.\n" + ] + } + ], + "source": [ + "(Logo & 'organization in (\"datajoint\", \"bcm\")').delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "ext = schema.external['remote']" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
3fd4b79b-9fdd-95c0-4074-68614d97f7e1162Noneorganizations/logos/pydata.pngbc56979a-0b38-1a79-1dd5-9713198a87fb2019-09-20 20:29:40
56c820a9-e83c-02c9-8e76-7ba90819258813216Noneorganizations/logos/utah.png1c755e4b-a65f-5576-352e-729f0c5987da2019-09-20 20:29:40
6e7ba0be-2171-a2ab-163e-ff12b0943e8e36444Noneorganizations/logos/datajoint.png9e902f2f-726a-2da5-0a24-ee3b97892f8a2019-09-20 20:29:39
81dc7dae-bebc-6d38-b891-c48eee8e340a33734Noneorganizations/logos/ucsd.png914f5b55-ea69-7489-d45b-031bec3ecaa52019-09-20 20:29:40
8c25c65d-506d-f382-0e36-88e1f6057ea36293Noneorganizations/logos/pni.png0d59845d-5c75-24f8-20c8-9637517ed6a72019-09-20 20:29:39
f35b5a91-38cb-bc7f-ef17-b2ce94bb013183564Noneorganizations/logos/python.png3cf229ee-dc09-2549-277e-8859aad2fca52019-09-20 20:29:40
f97ea212-5794-926a-d852-deffc0f10bba227965Noneorganizations/logos/bcm.png10ee4617-a63f-61ba-2807-35a266a9f4882019-09-20 20:29:39
\n", + " \n", + "

Total: 7

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +--------+ +------------+ +------------+ +------------+ +------------+\n", + "3fd4b79b-9fdd- 162 None organizations/ bc56979a-0b38- 2019-09-20 20:\n", + "56c820a9-e83c- 13216 None organizations/ 1c755e4b-a65f- 2019-09-20 20:\n", + "6e7ba0be-2171- 36444 None organizations/ 9e902f2f-726a- 2019-09-20 20:\n", + "81dc7dae-bebc- 33734 None organizations/ 914f5b55-ea69- 2019-09-20 20:\n", + "8c25c65d-506d- 6293 None organizations/ 0d59845d-5c75- 2019-09-20 20:\n", + "f35b5a91-38cb- 83564 None organizations/ 3cf229ee-dc09- 2019-09-20 20:\n", + "f97ea212-5794- 227965 None organizations/ 10ee4617-a63f- 2019-09-20 20:\n", + " (Total: 7)" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ext" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
6e7ba0be-2171-a2ab-163e-ff12b0943e8e36444Noneorganizations/logos/datajoint.png9e902f2f-726a-2da5-0a24-ee3b97892f8a2019-09-20 20:29:39
f97ea212-5794-926a-d852-deffc0f10bba227965Noneorganizations/logos/bcm.png10ee4617-a63f-61ba-2807-35a266a9f4882019-09-20 20:29:39
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +--------+ +------------+ +------------+ +------------+ +------------+\n", + "6e7ba0be-2171- 36444 None organizations/ 9e902f2f-726a- 2019-09-20 20:\n", + "f97ea212-5794- 227965 None organizations/ 10ee4617-a63f- 2019-09-20 20:\n", + " (Total: 2)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ext.unused()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(UUID('6e7ba0be-2171-a2ab-163e-ff12b0943e8e'),\n", + " PurePosixPath('code-clinic/organizations/logos/datajoint.png')),\n", + " (UUID('f97ea212-5794-926a-d852-deffc0f10bba'),\n", + " PurePosixPath('code-clinic/organizations/logos/bcm.png'))]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ext.unused().fetch_external_paths()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 2/2 [00:00<00:00, 67.15it/s]\n" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ext.delete()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------+ +------+ +------------+ +----------+ +------------+ +-----------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ext.unused()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "states = dict(\n", + " AL='Alabama', AK='Alaska', AZ='Arizona', AR='Arkansas',\n", + " CA='California', CO='Colorado', CT='Connecticut', DE='Delaware',\n", + " FL='Florida', GA='Georgia', HI='Hawaii', ID='Idaho', \n", + " IL='Illinois', IN='Indiana', IA='Iowa', KS='Kansas',\n", + " KY='Kentucky', LA='Louisiana', ME='Maine', MD='Maryland',\n", + " MA='Massachusetts', MI='Michigan', MN='Minnesota', MS='Mississippi',\n", + " MO='Missouri', MT='Montana', NE='Nebraska', NV='Nevada',\n", + " NH='New Hampshire', NJ='New Jersey', NM='New Mexico', NY='New York',\n", + " NC='North Carolina', ND='North Dakota', OH='Ohio', OK='Oklahoma',\n", + " OR='Oregon', PA='Pennsylvania', RI='Rhode Island', SC='South Carlina',\n", + " SD='South Dakota', TN='Tennessee', TX='Texas', UT='Utah',\n", + " VT='Vermont', VA='Virginia', WA='Washington', WV='West Virginia', \n", + " WI='Wisconsin', WY='Wyoming')" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class State(dj.Lookup):\n", + " definition = \"\"\"\n", + " # United States\n", + " state_code : char(2)\n", + " ---\n", + " state : varchar(20)\n", + " \"\"\"\n", + " contents = states.items()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " United States\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

state_code

\n", + " \n", + "
\n", + "

state

\n", + " \n", + "
AKAlaska
ALAlabama
ARArkansas
AZArizona
CACalifornia
COColorado
CTConnecticut
\n", + "

...

\n", + "

Total: 50

\n", + " " + ], + "text/plain": [ + "*state_code state \n", + "+------------+ +------------+\n", + "AK Alaska \n", + "AL Alabama \n", + "AR Arkansas \n", + "AZ Arizona \n", + "CA California \n", + "CO Colorado \n", + "CT Connecticut \n", + " ...\n", + " (Total: 50)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "State()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class StateBird(dj.Imported):\n", + " definition = \"\"\"\n", + " -> State\n", + " ---\n", + " bird_image : filepath@remote \n", + " \"\"\"\n", + " path = os.path.join(dj.config['stores']['remote']['stage'], 'states', 'birds')\n", + " \n", + " \n", + " def make(self, key):\n", + " os.makedirs(self.path, exist_ok=True)\n", + " state = (State & key).fetch1('state')\n", + " url = \"http://www.theus50.com/images/state-birds/{state}-bird.jpg\".format(state=state.lower())\n", + " local_file = os.path.join(self.path, state.lower() + os.path.splitext(url)[1])\n", + " print(local_file)\n", + " with open(local_file, 'wb') as f:\n", + " f.write(requests.get(url).content)\n", + " self.insert1(dict(key, bird_image=local_file)) \n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/alaska.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/alabama.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/arkansas.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/arizona.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/california.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/colorado.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/connecticut.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/delaware.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/florida.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/georgia.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/hawaii.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/iowa.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/idaho.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/illinois.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/indiana.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/kansas.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/kentucky.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/louisiana.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/massachusetts.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/maryland.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/maine.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/michigan.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/minnesota.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/missouri.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/mississippi.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/montana.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/north carolina.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/north dakota.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/nebraska.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/new hampshire.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/new jersey.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/new mexico.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/nevada.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/new york.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/ohio.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/oklahoma.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/oregon.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/pennsylvania.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/rhode island.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/south carlina.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/south dakota.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/tennessee.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/texas.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/utah.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/virginia.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/vermont.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/washington.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/wisconsin.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/west virginia.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/birds/wyoming.jpg\n" + ] + } + ], + "source": [ + "StateBird.populate()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class StateFlower(dj.Imported):\n", + " definition = \"\"\"\n", + " -> State\n", + " ---\n", + " flower_image : filepath@remote \n", + " \"\"\"\n", + " path = os.path.join(dj.config['stores']['remote']['stage'],'states', 'flowers')\n", + " \n", + " \n", + " def make(self, key):\n", + " os.makedirs(self.path, exist_ok=True)\n", + " state = (State & key).fetch1('state')\n", + " url = \"http://www.theus50.com/images/state-birds/{state}-flower.jpg\".format(state=state.lower())\n", + " local_file = os.path.join(self.path, state.lower() + os.path.splitext(url)[1])\n", + " print(local_file)\n", + " with open(local_file, 'wb') as f:\n", + " f.write(requests.get(url).content)\n", + " self.insert1(dict(key, flower_image=local_file)) " + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/alaska.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/alabama.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/arkansas.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/arizona.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/california.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/colorado.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/connecticut.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/delaware.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/florida.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/georgia.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/hawaii.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/iowa.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/idaho.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/illinois.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/indiana.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/kansas.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/kentucky.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/louisiana.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/massachusetts.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/maryland.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/maine.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/michigan.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/minnesota.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/missouri.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/mississippi.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/montana.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/north carolina.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/north dakota.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/nebraska.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/new hampshire.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/new jersey.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/new mexico.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/nevada.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/new york.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/ohio.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/oklahoma.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/oregon.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/pennsylvania.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/rhode island.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/south carlina.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/south dakota.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/tennessee.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/texas.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/utah.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/virginia.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/vermont.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/washington.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/wisconsin.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/west virginia.jpg\n", + "/home/dimitri/dev/db-programming-with-datajoint/notebooks/stage/states/flowers/wyoming.jpg\n" + ] + } + ], + "source": [ + "StateFlower().populate()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "StateFlower\n", + "\n", + "\n", + "StateFlower\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "State\n", + "\n", + "\n", + "State\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "State->StateFlower\n", + "\n", + "\n", + "\n", + "\n", + "StateBird\n", + "\n", + "\n", + "StateBird\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "State->StateBird\n", + "\n", + "\n", + "\n", + "\n", + "Organization\n", + "\n", + "\n", + "Organization\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Logo\n", + "\n", + "\n", + "Logo\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Organization->Logo\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(schema)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "External file tables for schema `dimitri_filepath`:\n", + " \"remote\" s3:code-clinic\"" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
01d9c579-f923-81d1-8f35-c7fddbf171f4417Nonestates/flowers/maine.jpg980488b3-8709-9958-e741-77e8b22e6aed2019-09-20 20:30:36
01f19f6d-e7d3-b718-8b1b-8122a211697325735Nonestates/birds/texas.jpg0c1f52a6-8cef-1534-3b5c-bb03f52f6b782019-09-20 20:30:27
02621cc5-6215-f043-2297-1844393d603b416Nonestates/flowers/iowa.jpg2e98d103-ff1a-387f-f84b-a4653fe69b292019-09-20 20:30:34
0315aac9-440c-ed4f-f6d1-1581547212be25142Nonestates/birds/indiana.jpg511d9eac-52a7-7a76-b60a-ed3131c088742019-09-20 20:30:23
0427f842-2a48-a302-af48-fc2973d8fbbe25142Nonestates/birds/kentucky.jpg511d9eac-52a7-7a76-b60a-ed3131c088742019-09-20 20:30:24
056a7914-856d-3f8e-7880-6432ab313d58425Nonestates/flowers/west virginia.jpgbe37e4f6-93d3-3aa1-f326-b4c765c106482019-09-20 20:30:39
09adf4ec-d9c7-ee18-868e-e577a9ea8731422Nonestates/birds/south dakota.jpgf6df925d-149e-9629-4d2d-82a6786fd92b2019-09-20 20:30:27
\n", + "

...

\n", + "

Total: 105

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------------+ +-------+ +------------+ +------------+ +------------+ +------------+\n", + "01d9c579-f923- 417 None states/flowers 980488b3-8709- 2019-09-20 20:\n", + "01f19f6d-e7d3- 25735 None states/birds/t 0c1f52a6-8cef- 2019-09-20 20:\n", + "02621cc5-6215- 416 None states/flowers 2e98d103-ff1a- 2019-09-20 20:\n", + "0315aac9-440c- 25142 None states/birds/i 511d9eac-52a7- 2019-09-20 20:\n", + "0427f842-2a48- 25142 None states/birds/k 511d9eac-52a7- 2019-09-20 20:\n", + "056a7914-856d- 425 None states/flowers be37e4f6-93d3- 2019-09-20 20:\n", + "09adf4ec-d9c7- 422 None states/birds/s f6df925d-149e- 2019-09-20 20:\n", + " ...\n", + " (Total: 105)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['remote']" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(UUID('01d9c579-f923-81d1-8f35-c7fddbf171f4'),\n", + " PurePosixPath('code-clinic/states/flowers/maine.jpg')),\n", + " (UUID('01f19f6d-e7d3-b718-8b1b-8122a2116973'),\n", + " PurePosixPath('code-clinic/states/birds/texas.jpg')),\n", + " (UUID('02621cc5-6215-f043-2297-1844393d603b'),\n", + " PurePosixPath('code-clinic/states/flowers/iowa.jpg')),\n", + " (UUID('0315aac9-440c-ed4f-f6d1-1581547212be'),\n", + " PurePosixPath('code-clinic/states/birds/indiana.jpg')),\n", + " (UUID('0427f842-2a48-a302-af48-fc2973d8fbbe'),\n", + " PurePosixPath('code-clinic/states/birds/kentucky.jpg')),\n", + " (UUID('056a7914-856d-3f8e-7880-6432ab313d58'),\n", + " PurePosixPath('code-clinic/states/flowers/west virginia.jpg')),\n", + " (UUID('09adf4ec-d9c7-ee18-868e-e577a9ea8731'),\n", + " PurePosixPath('code-clinic/states/birds/south dakota.jpg')),\n", + " (UUID('0e1c29e6-6bf1-5d23-1e06-cc9ee05d79d9'),\n", + " PurePosixPath('code-clinic/states/birds/north dakota.jpg')),\n", + " (UUID('114da747-fee3-a47d-e90b-be2e1cc0bfe0'),\n", + " PurePosixPath('code-clinic/states/flowers/washington.jpg')),\n", + " (UUID('17f485a8-eca3-d740-9100-7bc6caae9faa'),\n", + " PurePosixPath('code-clinic/states/birds/new york.jpg')),\n", + " (UUID('184bd7b2-9885-89dd-7da6-e425fbe535f2'),\n", + " PurePosixPath('code-clinic/states/flowers/missouri.jpg')),\n", + " (UUID('1b0a55dd-344c-e06e-7bfe-7e0f5ca5c188'),\n", + " PurePosixPath('code-clinic/states/birds/minnesota.jpg')),\n", + " (UUID('1e9a63ab-7dd3-9735-2e4c-d03b679709c1'),\n", + " PurePosixPath('code-clinic/states/birds/michigan.jpg')),\n", + " (UUID('22647fa9-bcd3-417b-797a-2c98ea6904aa'),\n", + " PurePosixPath('code-clinic/states/flowers/virginia.jpg')),\n", + " (UUID('246b2008-8f03-31e0-6d34-db8ccfc39494'),\n", + " PurePosixPath('code-clinic/states/birds/maine.jpg')),\n", + " (UUID('24fcd4ca-a9eb-ca77-b1bd-9430c6fbb51b'),\n", + " PurePosixPath('code-clinic/states/flowers/california.jpg')),\n", + " (UUID('277679c0-f7b2-f54c-1d3b-f19e8b0d15f9'),\n", + " PurePosixPath('code-clinic/states/flowers/massachusetts.jpg')),\n", + " (UUID('2a34aa82-edb9-5064-b773-0b6d9e51cdb0'),\n", + " PurePosixPath('code-clinic/states/flowers/north carolina.jpg')),\n", + " (UUID('2ba8d00e-c4f2-b86e-6870-c4a51395e6f8'),\n", + " PurePosixPath('code-clinic/states/flowers/new hampshire.jpg')),\n", + " (UUID('2bbb860f-df7b-cd91-aa42-9e97cbc1dc9d'),\n", + " PurePosixPath('code-clinic/states/flowers/north dakota.jpg')),\n", + " (UUID('2ed31b79-217e-63b9-6ef0-91fcd16eb054'),\n", + " PurePosixPath('code-clinic/states/flowers/michigan.jpg')),\n", + " (UUID('325725fb-7ec0-23b5-d460-1e026b36dcdf'),\n", + " PurePosixPath('code-clinic/states/birds/ohio.jpg')),\n", + " (UUID('3347e424-aa9c-b85f-a8d6-50fd93c8b264'),\n", + " PurePosixPath('code-clinic/states/flowers/florida.jpg')),\n", + " (UUID('33b1c381-4cad-6437-42a8-5bcbd1751e6c'),\n", + " PurePosixPath('code-clinic/states/birds/vermont.jpg')),\n", + " (UUID('33ed18b0-d09f-334b-62ef-0f83151db34b'),\n", + " PurePosixPath('code-clinic/states/flowers/wisconsin.jpg')),\n", + " (UUID('341157e6-d8db-703a-1953-7e422666a173'),\n", + " PurePosixPath('code-clinic/states/birds/massachusetts.jpg')),\n", + " (UUID('39a1a53e-a326-0bde-5500-c17024e7703f'),\n", + " PurePosixPath('code-clinic/states/flowers/mississippi.jpg')),\n", + " (UUID('3c7d8b20-e576-a604-97ec-f2ca692651dd'),\n", + " PurePosixPath('code-clinic/states/birds/illinois.jpg')),\n", + " (UUID('3e382bd8-1d25-6f10-a88a-4b7fa69fcfc1'),\n", + " PurePosixPath('code-clinic/states/birds/alabama.jpg')),\n", + " (UUID('3fd4b79b-9fdd-95c0-4074-68614d97f7e1'),\n", + " PurePosixPath('code-clinic/organizations/logos/pydata.png')),\n", + " (UUID('428ccfc3-837d-fb1f-8f71-fa2b1fbd5fe5'),\n", + " PurePosixPath('code-clinic/states/flowers/vermont.jpg')),\n", + " (UUID('438a5157-bf38-c247-bf71-0a6633eb3fb6'),\n", + " PurePosixPath('code-clinic/states/birds/oregon.jpg')),\n", + " (UUID('459d0e6e-be54-3802-2be9-bbb4d0c21e7c'),\n", + " PurePosixPath('code-clinic/states/flowers/colorado.jpg')),\n", + " (UUID('45b1800a-7a87-3ad7-70d6-2aad9ad8981b'),\n", + " PurePosixPath('code-clinic/states/birds/north carolina.jpg')),\n", + " (UUID('4914c53d-ab3f-d6b3-59c7-790657984757'),\n", + " PurePosixPath('code-clinic/states/birds/wisconsin.jpg')),\n", + " (UUID('4a398dae-9893-d31b-867c-c64def9ac541'),\n", + " PurePosixPath('code-clinic/states/flowers/texas.jpg')),\n", + " (UUID('4ad20148-4af0-97b1-65e3-47b73f988a00'),\n", + " PurePosixPath('code-clinic/states/birds/washington.jpg')),\n", + " (UUID('51f95d0b-7528-1840-1b4f-ffbe04286f9a'),\n", + " PurePosixPath('code-clinic/states/flowers/nebraska.jpg')),\n", + " (UUID('56107d48-aa13-e4eb-773d-7b33c2909fe9'),\n", + " PurePosixPath('code-clinic/states/birds/virginia.jpg')),\n", + " (UUID('56c820a9-e83c-02c9-8e76-7ba908192588'),\n", + " PurePosixPath('code-clinic/organizations/logos/utah.png')),\n", + " (UUID('587ad22e-14a9-b41d-fbb7-13d85702fbd6'),\n", + " PurePosixPath('code-clinic/states/flowers/kansas.jpg')),\n", + " (UUID('5ded7dea-8fa2-2450-419d-7fb24ee581e9'),\n", + " PurePosixPath('code-clinic/states/flowers/south carlina.jpg')),\n", + " (UUID('61a1dd90-eda9-69de-a7a0-cdbd1627fed1'),\n", + " PurePosixPath('code-clinic/states/flowers/utah.jpg')),\n", + " (UUID('628be6e1-f489-2521-7eca-f8690d410019'),\n", + " PurePosixPath('code-clinic/states/birds/iowa.jpg')),\n", + " (UUID('6338bb2d-1000-a5ae-3ff2-27deb02171f1'),\n", + " PurePosixPath('code-clinic/states/birds/mississippi.jpg')),\n", + " (UUID('637e4577-8098-45d2-ac8f-5d95919a9ccf'),\n", + " PurePosixPath('code-clinic/states/birds/connecticut.jpg')),\n", + " (UUID('6b1cd553-5072-0702-98e7-fbc89022bccb'),\n", + " PurePosixPath('code-clinic/states/flowers/georgia.jpg')),\n", + " (UUID('6f8df4a5-06f0-5f89-c08d-42b97c192ef4'),\n", + " PurePosixPath('code-clinic/states/birds/alaska.jpg')),\n", + " (UUID('6fa95b8c-46e1-ca63-6610-142ab0bcb4af'),\n", + " PurePosixPath('code-clinic/states/birds/louisiana.jpg')),\n", + " (UUID('6fb24649-c923-678d-67e1-331b42a2a024'),\n", + " PurePosixPath('code-clinic/states/birds/oklahoma.jpg')),\n", + " (UUID('7328d3ca-ccda-bbf5-2d56-1da4b58aaba0'),\n", + " PurePosixPath('code-clinic/states/birds/wyoming.jpg')),\n", + " (UUID('75482e52-f840-adc2-0a36-4a50c777bab5'),\n", + " PurePosixPath('code-clinic/states/birds/new jersey.jpg')),\n", + " (UUID('7718d696-e868-e61e-fb8c-c2e9236f45a6'),\n", + " PurePosixPath('code-clinic/states/birds/nevada.jpg')),\n", + " (UUID('7811a892-4744-15ec-ea51-a3b6562c4396'),\n", + " PurePosixPath('code-clinic/states/flowers/alaska.jpg')),\n", + " (UUID('79b941dd-4fbc-a80b-b20e-db91c29b2c40'),\n", + " PurePosixPath('code-clinic/states/flowers/indiana.jpg')),\n", + " (UUID('7e5ade5c-f149-1b0d-b4cd-2a1afa4affc2'),\n", + " PurePosixPath('code-clinic/states/flowers/delaware.jpg')),\n", + " (UUID('7f21244c-8204-20a6-cc87-2dc5a622132c'),\n", + " PurePosixPath('code-clinic/states/flowers/wyoming.jpg')),\n", + " (UUID('7fdc33b3-c7cd-ce42-b662-63796b41656f'),\n", + " PurePosixPath('code-clinic/states/birds/montana.jpg')),\n", + " (UUID('80514116-f2d3-f82a-f911-a8d3a0244305'),\n", + " PurePosixPath('code-clinic/states/birds/utah.jpg')),\n", + " (UUID('806c070a-5bf9-42ff-8174-df2b8b850800'),\n", + " PurePosixPath('code-clinic/states/birds/arizona.jpg')),\n", + " (UUID('8120cada-e54c-a91b-a978-b0aee4c92b82'),\n", + " PurePosixPath('code-clinic/states/flowers/connecticut.jpg')),\n", + " (UUID('81dc7dae-bebc-6d38-b891-c48eee8e340a'),\n", + " PurePosixPath('code-clinic/organizations/logos/ucsd.png')),\n", + " (UUID('86bbbf4b-fa19-6aaa-66bc-321584f267b0'),\n", + " PurePosixPath('code-clinic/states/birds/hawaii.jpg')),\n", + " (UUID('870b42da-bae7-1d04-6b71-14d3c20c8db3'),\n", + " PurePosixPath('code-clinic/states/flowers/oregon.jpg')),\n", + " (UUID('88aeea31-58c4-786e-9741-0318b2a91ea0'),\n", + " PurePosixPath('code-clinic/states/birds/georgia.jpg')),\n", + " (UUID('8c25c65d-506d-f382-0e36-88e1f6057ea3'),\n", + " PurePosixPath('code-clinic/organizations/logos/pni.png')),\n", + " (UUID('8ecc5fb1-2de7-405d-e753-17707a8efb6a'),\n", + " PurePosixPath('code-clinic/states/birds/colorado.jpg')),\n", + " (UUID('932b2892-f641-77c1-4bd6-29ab4a5435dc'),\n", + " PurePosixPath('code-clinic/states/flowers/oklahoma.jpg')),\n", + " (UUID('9b87e3d4-90a6-1222-c971-f08e5bbf7f35'),\n", + " PurePosixPath('code-clinic/states/birds/florida.jpg')),\n", + " (UUID('9ca7acff-e7fa-6132-aa34-235ff5518644'),\n", + " PurePosixPath('code-clinic/states/flowers/new jersey.jpg')),\n", + " (UUID('9d76c6a9-6806-30f0-c5bf-7c1927e98f17'),\n", + " PurePosixPath('code-clinic/states/birds/arkansas.jpg')),\n", + " (UUID('9d7d448d-d6d0-9518-bdc6-9f85b636c8fc'),\n", + " PurePosixPath('code-clinic/states/birds/delaware.jpg')),\n", + " (UUID('a108e5b1-7a55-063b-95ba-a4adad6c5724'),\n", + " PurePosixPath('code-clinic/states/birds/west virginia.jpg')),\n", + " (UUID('a3ae8749-8636-d957-4037-0cca46084e19'),\n", + " PurePosixPath('code-clinic/states/birds/idaho.jpg')),\n", + " (UUID('a48c55fa-05e8-0928-1976-a3b4bf405115'),\n", + " PurePosixPath('code-clinic/states/flowers/louisiana.jpg')),\n", + " (UUID('a65c4bab-cf70-e4c6-3cc3-ed7e1bcc4e1a'),\n", + " PurePosixPath('code-clinic/states/birds/missouri.jpg')),\n", + " (UUID('a9327cbb-baf1-166a-175a-d4c1da6eb8a6'),\n", + " PurePosixPath('code-clinic/states/flowers/alabama.jpg')),\n", + " (UUID('a9cc8108-1273-dac5-b7c8-db290b6c53cb'),\n", + " PurePosixPath('code-clinic/states/flowers/tennessee.jpg')),\n", + " (UUID('ab5d0504-86a5-bb98-5ebc-b1ebb2cd5a9d'),\n", + " PurePosixPath('code-clinic/states/birds/nebraska.jpg')),\n", + " (UUID('b1349a3d-a02a-2b02-ccf9-2750039d5831'),\n", + " PurePosixPath('code-clinic/states/flowers/kentucky.jpg')),\n", + " (UUID('b6973144-e881-ff72-a5f3-8325351e3068'),\n", + " PurePosixPath('code-clinic/states/birds/tennessee.jpg')),\n", + " (UUID('bd23ff8c-619d-4470-8c19-1b1d4fbe228e'),\n", + " PurePosixPath('code-clinic/states/birds/rhode island.jpg')),\n", + " (UUID('beccafba-60c5-dafa-20dc-432197ea4ad9'),\n", + " PurePosixPath('code-clinic/states/birds/maryland.jpg')),\n", + " (UUID('bf223a8a-f627-72da-5bf5-3bc53e78babd'),\n", + " PurePosixPath('code-clinic/states/flowers/ohio.jpg')),\n", + " (UUID('bf9db2bc-13c7-aa5b-d2ed-80e6a77dc3c7'),\n", + " PurePosixPath('code-clinic/states/flowers/south dakota.jpg')),\n", + " (UUID('bfb01da0-82a6-31de-cfd3-ea3dd79863ad'),\n", + " PurePosixPath('code-clinic/states/birds/new mexico.jpg')),\n", + " (UUID('c172bb0c-434e-ad47-81ce-2d7861eb4dec'),\n", + " PurePosixPath('code-clinic/states/birds/kansas.jpg')),\n", + " (UUID('c2bac2f9-74bb-be06-1f43-9badf662ae42'),\n", + " PurePosixPath('code-clinic/states/flowers/hawaii.jpg')),\n", + " (UUID('c30057d1-8f4f-13aa-6f79-3d88c25f31ad'),\n", + " PurePosixPath('code-clinic/states/birds/pennsylvania.jpg')),\n", + " (UUID('c3d92ad3-be39-8416-f632-6683842428ed'),\n", + " PurePosixPath('code-clinic/states/flowers/rhode island.jpg')),\n", + " (UUID('d2131650-6079-7909-89e0-085cb30f27ea'),\n", + " PurePosixPath('code-clinic/states/flowers/arizona.jpg')),\n", + " (UUID('d2351cc0-2379-882a-a0e0-875574b6a1f2'),\n", + " PurePosixPath('code-clinic/states/birds/california.jpg')),\n", + " (UUID('e114c51c-e671-53ba-bde2-6d1bdaf478f9'),\n", + " PurePosixPath('code-clinic/states/birds/new hampshire.jpg')),\n", + " (UUID('e3afc8d6-3136-5008-ee53-78815b6b7cc3'),\n", + " PurePosixPath('code-clinic/states/flowers/arkansas.jpg')),\n", + " (UUID('eba07400-1c2a-d25d-9b7a-84da614f9ae5'),\n", + " PurePosixPath('code-clinic/states/flowers/maryland.jpg')),\n", + " (UUID('f35b5a91-38cb-bc7f-ef17-b2ce94bb0131'),\n", + " PurePosixPath('code-clinic/organizations/logos/python.png')),\n", + " (UUID('f3d173a2-3998-e56e-3187-d72b9a80e143'),\n", + " PurePosixPath('code-clinic/states/flowers/minnesota.jpg')),\n", + " (UUID('f47f158e-6f86-8863-1999-da771defb285'),\n", + " PurePosixPath('code-clinic/states/flowers/idaho.jpg')),\n", + " (UUID('f48977ed-bb21-506f-3e93-76c6c6abb0cf'),\n", + " PurePosixPath('code-clinic/states/flowers/pennsylvania.jpg')),\n", + " (UUID('f501a5b4-5689-45fb-f062-b33063167bfc'),\n", + " PurePosixPath('code-clinic/states/flowers/new york.jpg')),\n", + " (UUID('f509ca79-2169-19fc-b7bc-37d8a3e3c4c8'),\n", + " PurePosixPath('code-clinic/states/flowers/montana.jpg')),\n", + " (UUID('f78e4d0e-ed71-734f-86d2-7de0e070dc96'),\n", + " PurePosixPath('code-clinic/states/flowers/new mexico.jpg')),\n", + " (UUID('f83d03ce-4e72-7e80-0a49-b0f03479d62c'),\n", + " PurePosixPath('code-clinic/states/flowers/illinois.jpg')),\n", + " (UUID('f96070ba-836b-5f3a-2151-73340ba0b5e0'),\n", + " PurePosixPath('code-clinic/states/birds/south carlina.jpg')),\n", + " (UUID('fd45e697-bdc4-3408-56e0-2709469f6b7f'),\n", + " PurePosixPath('code-clinic/states/flowers/nevada.jpg'))]" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['remote'].fetch_external_paths()" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " external storage tracking\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

hash

\n", + " hash of contents (blob), of filename + contents (attach), or relative filepath (filepath)\n", + "
\n", + "

size

\n", + " size of object in bytes\n", + "
\n", + "

attachment_name

\n", + " the filename of an attachment\n", + "
\n", + "

filepath

\n", + " relative filepath or attachment filename\n", + "
\n", + "

contents_hash

\n", + " used for the filepath datatype\n", + "
\n", + "

timestamp

\n", + " automatic timestamp\n", + "
\n", + " \n", + "

Total: 0

\n", + " " + ], + "text/plain": [ + "*hash size attachment_nam filepath contents_hash timestamp \n", + "+------+ +------+ +------------+ +----------+ +------------+ +-----------+\n", + "\n", + " (Total: 0)" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "schema.external['remote'].unused()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Remaining issues:\n", + "\n", + "* Re-inserting the same filepath twice with different contents throws and error. This is fixed by deleting the unused external table entries.\n", + "* Users may need the option to fetch only the paths or object handles without downloading the files. Fetch option or config?\n", + "* Dropping the schema leaves orphaned externals. However, dropping tables works normally.\n", + "* Insert(query) with externals has not tested." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Getting Started with DataJoint.ipynb b/notebooks/Getting Started with DataJoint.ipynb index 1d3f470..3f14c44 100644 --- a/notebooks/Getting Started with DataJoint.ipynb +++ b/notebooks/Getting Started with DataJoint.ipynb @@ -25,12 +25,23 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dbadmin@dimitri-proj0.cda95qzjbnvs.us-east-1.rds.amazonaws.com:3306\n", + "Proceed to delete entire schema `university`? [yes, No]: yes\n" + ] + } + ], "source": [ + "schema = dj.schema('university')\n", + "schema.drop()\n", "schema = dj.schema('university')" ] }, @@ -43,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": { "scrolled": false }, @@ -69,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -82,87 +93,86 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
1
\n", + " \n", + " \n", "\n", "\n", "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
1AliceAndersonF
\n", - " \n", - "

Total: 1

\n", - " " + " \n", + " \n", + "

Total: 1

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex \n", @@ -171,7 +181,7 @@ " (Total: 1)" ] }, - "execution_count": 8, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -189,7 +199,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -198,90 +208,89 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
1
\n", + " \n", + " \n", "\n", "\n", "\n", "\n", "\n", "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
1AliceAndersonF
2BobDylanM
\n", - " \n", - "

Total: 2

\n", - " " + " \n", + " \n", + "

Total: 2

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex \n", @@ -291,7 +300,7 @@ " (Total: 2)" ] }, - "execution_count": 10, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -302,7 +311,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -311,80 +320,79 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
1
\n", + " \n", + " \n", "\n", "\n", "\n", @@ -394,10 +402,10 @@ "\n", "\n", "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
1AliceAndersonF
2CarolLewisF
\n", - " \n", - "

Total: 3

\n", - " " + " \n", + " \n", + "

Total: 3

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex \n", @@ -408,7 +416,7 @@ " (Total: 3)" ] }, - "execution_count": 12, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -426,7 +434,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -440,80 +448,79 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
1
\n", + " \n", + " \n", "\n", "\n", "\n", @@ -532,10 +539,10 @@ "\n", "\n", "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
1AliceAndersonF
2MaxScottM
\n", - " \n", - "

Total: 6

\n", - " " + " \n", + " \n", + "

Total: 6

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex \n", @@ -549,7 +556,7 @@ " (Total: 6)" ] }, - "execution_count": 15, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -567,7 +574,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -581,80 +588,79 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
1
\n", + " \n", + " \n", "\n", "\n", "\n", @@ -682,10 +688,10 @@ "\n", "\n", "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
1AliceAndersonF
2EmmaReedF
\n", - " \n", - "

Total: 9

\n", - " " + " \n", + " \n", + "

Total: 9

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex \n", @@ -702,7 +708,7 @@ " (Total: 9)" ] }, - "execution_count": 18, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -720,7 +726,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -739,7 +745,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -762,7 +768,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -771,7 +777,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -780,231 +786,230 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
\n", - "

date_of_birth

\n", - " \n", - "
\n", - "

home_address

\n", - " mailing street address\n", - "
\n", - "

home_city

\n", - " mailing address\n", - "
\n", - "

home_state

\n", - " US state acronym: e.g. OH\n", - "
\n", - "

home_zip

\n", - " zipcode e.g. 93979-4979\n", - "
\n", - "

home_phone

\n", - " e.g. 414.657.6883x0881\n", - "
0MatthewMilesM1985-11-11279 Joseph Estate Apt. 874MasonstadWA26544830-541-2678
1WillieMayM1995-04-215509 Cross CanyonWest ToddNE512445774925367
2DavidBoone
\n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

date_of_birth

\n", + " \n", + "
\n", + "

home_address

\n", + " mailing street address\n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
\n", + "

home_zip

\n", + " zipcode e.g. 93979-4979\n", + "
\n", + "

home_phone

\n", + " e.g. 414.657.6883x0881\n", + "
0JillianFischerF2000-12-1468627 Rodriguez Center Suite 032East AnthonyWY64660+1-837-213-8181x775
1DanielleMccannF2001-07-10459 Watts Path Suite 309Lake CharlesPA422262367757570
2GabrielAustinM1984-12-1703892 Amy Rapid Apt. 123MaryburyOK21149164.601.2816
3DerekBarrett2002-05-1559647 Bruce GroveNew AlexanderOR48511+1-834-046-2990x923
3GaryAdamsM2000-07-0493045 Pamela PlainNorth BradymouthSD33618519-951-7205x52305
4AlbertDavis1990-12-302740 Henderson Shore Suite 497East KyleVA16432(652)366-4084x188
4AaronDixonM1994-01-15442 Jacob Bypass Apt. 357Lake BrendanCT87296977-228-5606x062
5ErikaPhillips2001-06-1857205 Clark LaneMillermouthAZ90284001-593-504-9751x736
5MackenzieBoyerF1988-03-31911 Tiffany Pike Apt. 570ChristophertownUT56135(889)801-9551x4395
6LucasThomas1988-12-269458 Ashley Stravenue Apt. 044Port MelissalandOH54499669-684-3236
6BradleyWilliamsM2002-01-05810 Williams Dale Apt. 270Port Hannahport1986-03-062067 Gonzalez Summit Suite 002New KaylaAK668751407435812
7NancyFoxF1984-05-1696581 Jackson PortsLake IanmouthLA320465707101635
8DeborahVincent128230458569751
7AnthonyDiazM1990-12-3176723 Anthony CoveSabrinaburyOR42013+1-183-571-0712
8JacquelineColonF2001-01-10237 Elizabeth Pass Suite 178North Jennifer1996-02-2464635 Duncan PlaceSolisburghUT37335+1-696-997-1635x317
9StephanieKoch76301(983)027-5735x055
9NicoleLongF1993-06-128303 Tiffany RestWilsonviewMA14163001-917-947-1447x650
10RobertCooper1986-10-24804 Sarah Rue Suite 661Port KristinamouthND07732568-928-6900x28524
10BrandonGrantM1988-07-142070 Christopher KnollsAndreastadTN13995957-517-9026x7264
11DonnaWilcoxF1990-05-25540 Alexis GreenEast RachaelviewMO329759502795098
\n", - "

...

\n", - "

Total: 900

\n", - " " + "1997-07-31\n", + "245 Christian Well\n", + "Ericside\n", + "ID\n", + "94059\n", + "626711359211\n", + "Kevin\n", + "Walker\n", + "M\n", + "2000-01-16\n", + "12139 Smith Hills\n", + "Coleshire\n", + "OK\n", + "82926\n", + "001-227-524-1444x871 \n", + " \n", + "

...

\n", + "

Total: 300

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex date_of_birth home_address home_city home_state home_zip home_phone \n", "+------------+ +------------+ +-----------+ +-----+ +------------+ +------------+ +------------+ +------------+ +----------+ +------------+\n", - "0 Matthew Miles M 1985-11-11 279 Joseph Est Masonstad WA 26544 830-541-2678 \n", - "1 Willie May M 1995-04-21 5509 Cross Can West Todd NE 51244 5774925367 \n", - "2 David Boone M 1984-12-17 03892 Amy Rapi Marybury OK 21149 164.601.2816 \n", - "3 Derek Barrett M 2000-07-04 93045 Pamela P North Bradymou SD 33618 519-951-7205x5\n", - "4 Albert Davis M 1994-01-15 442 Jacob Bypa Lake Brendan CT 87296 977-228-5606x0\n", - "5 Erika Phillips F 1988-03-31 911 Tiffany Pi Christophertow UT 56135 (889)801-9551x\n", - "6 Lucas Thomas M 2002-01-05 810 Williams D Port Hannahpor AK 66875 1407435812 \n", - "7 Nancy Fox F 1984-05-16 96581 Jackson Lake Ianmouth LA 32046 5707101635 \n", - "8 Deborah Vincent F 2001-01-10 237 Elizabeth North Jennifer UT 37335 +1-696-997-163\n", - "9 Stephanie Koch F 1993-06-12 8303 Tiffany R Wilsonview MA 14163 001-917-947-14\n", - "10 Robert Cooper M 1988-07-14 2070 Christoph Andreastad TN 13995 957-517-9026x7\n", - "11 Donna Wilcox F 1990-05-25 540 Alexis Gre East Rachaelvi MO 32975 9502795098 \n", + "0 Jillian Fischer F 2000-12-14 68627 Rodrigue East Anthony WY 64660 +1-837-213-818\n", + "1 Danielle Mccann F 2001-07-10 459 Watts Path Lake Charles PA 42226 2367757570 \n", + "2 Gabriel Austin M 2002-05-15 59647 Bruce Gr New Alexander OR 48511 +1-834-046-299\n", + "3 Gary Adams M 1990-12-30 2740 Henderson East Kyle VA 16432 (652)366-4084x\n", + "4 Aaron Dixon M 2001-06-18 57205 Clark La Millermouth AZ 90284 001-593-504-97\n", + "5 Mackenzie Boyer F 1988-12-26 9458 Ashley St Port Melissala OH 54499 669-684-3236 \n", + "6 Bradley Williams M 1986-03-06 2067 Gonzalez New Kayla AK 12823 0458569751 \n", + "7 Anthony Diaz M 1990-12-31 76723 Anthony Sabrinabury OR 42013 +1-183-571-071\n", + "8 Jacqueline Colon F 1996-02-24 64635 Duncan P Solisburgh UT 76301 (983)027-5735x\n", + "9 Nicole Long F 1986-10-24 804 Sarah Rue Port Kristinam ND 07732 568-928-6900x2\n", + "10 Brandon Grant M 1997-07-31 245 Christian Ericside ID 94059 6267113592 \n", + "11 Kevin Walker M 2000-01-16 12139 Smith Hi Coleshire OK 82926 001-227-524-14\n", " ...\n", - " (Total: 900)" + " (Total: 300)" ] }, - "execution_count": 26, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -1029,231 +1034,230 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
\n", - "

date_of_birth

\n", - " \n", - "
\n", - "

home_address

\n", - " mailing street address\n", - "
\n", - "

home_city

\n", - " mailing address\n", - "
\n", - "

home_state

\n", - " US state acronym: e.g. OH\n", - "
\n", - "

home_zip

\n", - " zipcode e.g. 93979-4979\n", - "
\n", - "

home_phone

\n", - " e.g. 414.657.6883x0881\n", - "
5ErikaPhillips
\n", + " \n", + " \n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

date_of_birth

\n", + " \n", + "
\n", + "

home_address

\n", + " mailing street address\n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
\n", + "

home_zip

\n", + " zipcode e.g. 93979-4979\n", + "
\n", + "

home_phone

\n", + " e.g. 414.657.6883x0881\n", + "
0JillianFischerF1988-03-31911 Tiffany Pike Apt. 570ChristophertownUT56135(889)801-9551x4395
7NancyFox2000-12-1468627 Rodriguez Center Suite 032East AnthonyWY64660+1-837-213-8181x775
1DanielleMccannF1984-05-1696581 Jackson PortsLake IanmouthLA320465707101635
8DeborahVincent2001-07-10459 Watts Path Suite 309Lake CharlesPA422262367757570
5MackenzieBoyerF2001-01-10237 Elizabeth Pass Suite 178North JenniferUT37335+1-696-997-1635x317
9StephanieKoch1988-12-269458 Ashley Stravenue Apt. 044Port MelissalandOH54499669-684-3236
8JacquelineColonF1993-06-128303 Tiffany RestWilsonviewMA14163001-917-947-1447x650
11DonnaWilcox1996-02-2464635 Duncan PlaceSolisburghUT76301(983)027-5735x055
9NicoleLongF1990-05-25540 Alexis GreenEast RachaelviewMO329759502795098
12DanielleRoth1986-10-24804 Sarah Rue Suite 661Port KristinamouthND07732568-928-6900x28524
13AnnaGoldenF1991-01-04697 Stacy WallSchmidtboroughME35610069.269.5700x454
18ChristineNewman1999-03-09177 Robles PortJamesmouthPA57615862.977.2190
14JessicaRamosF1999-02-2800473 Daniel Freeway Apt. 706New EdwardNM45981287-445-7199x999
19GabrielleCarr1996-08-138318 Kenneth LakesNorth MeganNJ61279(736)120-0323x9695
16HeidiLewisF2000-01-03097 Gina Junction Apt. 926New IsaacCT43134204.887.5818x900
21MonicaWilson1992-03-07810 Wanda Pines Suite 244NicholechesterWA39909001-467-054-8928x041
18DawnPhillipsF1993-11-136255 William DivideWest KathyviewNC65452632.749.3286x370
23KatieBrown2004-08-309205 Margaret Common Apt. 133East JamesCA35646+1-986-030-3766x9704
19BethNielsenF1985-03-299844 Joshua GardenSouth ElizabethMN92773(058)445-6195x48931
26TiffanyJohnson1989-10-1452211 Joseph CoursePort AmandabergWY77459001-289-285-4689
20MikaylaThomasF1997-09-252752 Valerie Passage Apt. 583KennedyshireNC03101255.083.4385
29CatherineCastro2003-10-033104 Riggs Radial Apt. 751MathewsviewTX41272772.655.0583
22KristenWileyF2000-05-08506 Sue FlatRobinsonmouthWY66233+1-820-511-6491
\n", - "

...

\n", - "

Total: 456

\n", - " " + "2003-06-21\n", + "743 Linda Street Suite 948\n", + "Christopherhaven\n", + "WI\n", + "29738\n", + "873-334-4318x117 \n", + " \n", + "

...

\n", + "

Total: 151

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex date_of_birth home_address home_city home_state home_zip home_phone \n", "+------------+ +------------+ +-----------+ +-----+ +------------+ +------------+ +------------+ +------------+ +----------+ +------------+\n", - "5 Erika Phillips F 1988-03-31 911 Tiffany Pi Christophertow UT 56135 (889)801-9551x\n", - "7 Nancy Fox F 1984-05-16 96581 Jackson Lake Ianmouth LA 32046 5707101635 \n", - "8 Deborah Vincent F 2001-01-10 237 Elizabeth North Jennifer UT 37335 +1-696-997-163\n", - "9 Stephanie Koch F 1993-06-12 8303 Tiffany R Wilsonview MA 14163 001-917-947-14\n", - "11 Donna Wilcox F 1990-05-25 540 Alexis Gre East Rachaelvi MO 32975 9502795098 \n", - "12 Danielle Roth F 1991-01-04 697 Stacy Wall Schmidtborough ME 35610 069.269.5700x4\n", - "18 Christine Newman F 1999-02-28 00473 Daniel F New Edward NM 45981 287-445-7199x9\n", - "19 Gabrielle Carr F 2000-01-03 097 Gina Junct New Isaac CT 43134 204.887.5818x9\n", - "21 Monica Wilson F 1993-11-13 6255 William D West Kathyview NC 65452 632.749.3286x3\n", - "23 Katie Brown F 1985-03-29 9844 Joshua Ga South Elizabet MN 92773 (058)445-6195x\n", - "26 Tiffany Johnson F 1997-09-25 2752 Valerie P Kennedyshire NC 03101 255.083.4385 \n", - "29 Catherine Castro F 2000-05-08 506 Sue Flat Robinsonmouth WY 66233 +1-820-511-649\n", + "0 Jillian Fischer F 2000-12-14 68627 Rodrigue East Anthony WY 64660 +1-837-213-818\n", + "1 Danielle Mccann F 2001-07-10 459 Watts Path Lake Charles PA 42226 2367757570 \n", + "5 Mackenzie Boyer F 1988-12-26 9458 Ashley St Port Melissala OH 54499 669-684-3236 \n", + "8 Jacqueline Colon F 1996-02-24 64635 Duncan P Solisburgh UT 76301 (983)027-5735x\n", + "9 Nicole Long F 1986-10-24 804 Sarah Rue Port Kristinam ND 07732 568-928-6900x2\n", + "13 Anna Golden F 1999-03-09 177 Robles Por Jamesmouth PA 57615 862.977.2190 \n", + "14 Jessica Ramos F 1996-08-13 8318 Kenneth L North Megan NJ 61279 (736)120-0323x\n", + "16 Heidi Lewis F 1992-03-07 810 Wanda Pine Nicholechester WA 39909 001-467-054-89\n", + "18 Dawn Phillips F 2004-08-30 9205 Margaret East James CA 35646 +1-986-030-376\n", + "19 Beth Nielsen F 1989-10-14 52211 Joseph C Port Amandaber WY 77459 001-289-285-46\n", + "20 Mikayla Thomas F 2003-10-03 3104 Riggs Rad Mathewsview TX 41272 772.655.0583 \n", + "22 Kristen Wiley F 2003-06-21 743 Linda Stre Christopherhav WI 29738 873-334-4318x1\n", " ...\n", - " (Total: 456)" + " (Total: 151)" ] }, - "execution_count": 27, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -1264,231 +1268,230 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
\n", - "

date_of_birth

\n", - " \n", - "
\n", - "

home_address

\n", - " mailing street address\n", - "
\n", - "

home_city

\n", - " mailing address\n", - "
\n", - "

home_state

\n", - " US state acronym: e.g. OH\n", - "
\n", - "

home_zip

\n", - " zipcode e.g. 93979-4979\n", - "
\n", - "

home_phone

\n", - " e.g. 414.657.6883x0881\n", - "
3DerekBarrettM2000-07-0493045 Pamela PlainNorth BradymouthSD33618519-951-7205x52305
6LucasThomasM2002-01-05810 Williams Dale Apt. 270Port HannahportAK668751407435812
8DeborahVincent
\n", + " \n", + " \n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

date_of_birth

\n", + " \n", + "
\n", + "

home_address

\n", + " mailing street address\n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
\n", + "

home_zip

\n", + " zipcode e.g. 93979-4979\n", + "
\n", + "

home_phone

\n", + " e.g. 414.657.6883x0881\n", + "
0JillianFischerF2001-01-10237 Elizabeth Pass Suite 178North JenniferUT37335+1-696-997-1635x317
13KyleNeal2000-12-1468627 Rodriguez Center Suite 032East AnthonyWY64660+1-837-213-8181x775
1DanielleMccannF2001-07-10459 Watts Path Suite 309Lake CharlesPA422262367757570
2GabrielAustinM2003-07-302079 Hayes DriveSouth Kimberlyhaven2002-05-1559647 Bruce GroveNew AlexanderOR48511+1-834-046-2990x923
4AaronDixonM2001-06-1857205 Clark LaneMillermouthAZ25811294.223.3023
15KyleKirby90284001-593-504-9751x736
11KevinWalkerM2003-01-1855693 Hudson UnderpassSouth AmytownVA27199366.457.5029x8187
19GabrielleCarrF2000-01-03097 Gina Junction Apt. 926New IsaacCT43134204.887.5818x900
22DennisShields2000-01-1612139 Smith HillsColeshireOK82926001-227-524-1444x871
12BradleyJohnsonM2003-08-204385 Jonathan Track Suite 093BenderviewCO53814+1-650-949-3288
27JasonCannon2002-10-2744057 Amber ClubMartinboroughWA965291246630797
17CharlesBurnsM2001-03-12575 Gross CrossingDominguezhavenIL195830305674234
29CatherineCastro2000-03-21374 David ForksWest TylervilleAR66484180-517-6772x0773
18DawnPhillipsF2000-05-08506 Sue FlatRobinsonmouthWY66233+1-820-511-6491
41BradSanchezM2002-04-0415681 Smith GreensWest AlexanderboroughNV82139030.223.4585x6904
42DebbieRocha2004-08-309205 Margaret Common Apt. 133East JamesCA35646+1-986-030-3766x9704
20MikaylaThomasF2001-01-149449 Johnson OvalBryanttownHI20252(298)437-5761x444
45AndrewRiveraM2003-12-2121697 Steve HarborsEast MeredithCT98221257.005.7228
\n", - "

...

\n", - "

Total: 200

\n", - " " + "2003-10-03\n", + "3104 Riggs Radial Apt. 751\n", + "Mathewsview\n", + "TX\n", + "41272\n", + "772.655.058322\n", + "Kristen\n", + "Wiley\n", + "F\n", + "2003-06-21\n", + "743 Linda Street Suite 948\n", + "Christopherhaven\n", + "WI\n", + "29738\n", + "873-334-4318x11725\n", + "Dr.\n", + "Ashley\n", + "F\n", + "2005-03-16\n", + "1004 Fuller Route Suite 401\n", + "South Gary\n", + "AR\n", + "14885\n", + "965.800.8174x1455929\n", + "Victoria\n", + "Rodriguez\n", + "F\n", + "2005-06-12\n", + "828 Lee Route\n", + "Lake Felicia\n", + "NC\n", + "44928\n", + "189.831.8711 \n", + " \n", + "

...

\n", + "

Total: 96

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex date_of_birth home_address home_city home_state home_zip home_phone \n", "+------------+ +------------+ +-----------+ +-----+ +------------+ +------------+ +------------+ +------------+ +----------+ +------------+\n", - "3 Derek Barrett M 2000-07-04 93045 Pamela P North Bradymou SD 33618 519-951-7205x5\n", - "6 Lucas Thomas M 2002-01-05 810 Williams D Port Hannahpor AK 66875 1407435812 \n", - "8 Deborah Vincent F 2001-01-10 237 Elizabeth North Jennifer UT 37335 +1-696-997-163\n", - "13 Kyle Neal M 2003-07-30 2079 Hayes Dri South Kimberly AZ 25811 294.223.3023 \n", - "15 Kyle Kirby M 2003-01-18 55693 Hudson U South Amytown VA 27199 366.457.5029x8\n", - "19 Gabrielle Carr F 2000-01-03 097 Gina Junct New Isaac CT 43134 204.887.5818x9\n", - "22 Dennis Shields M 2003-08-20 4385 Jonathan Benderview CO 53814 +1-650-949-328\n", - "27 Jason Cannon M 2001-03-12 575 Gross Cros Dominguezhaven IL 19583 0305674234 \n", - "29 Catherine Castro F 2000-05-08 506 Sue Flat Robinsonmouth WY 66233 +1-820-511-649\n", - "41 Brad Sanchez M 2002-04-04 15681 Smith Gr West Alexander NV 82139 030.223.4585x6\n", - "42 Debbie Rocha F 2001-01-14 9449 Johnson O Bryanttown HI 20252 (298)437-5761x\n", - "45 Andrew Rivera M 2003-12-21 21697 Steve Ha East Meredith CT 98221 257.005.7228 \n", + "0 Jillian Fischer F 2000-12-14 68627 Rodrigue East Anthony WY 64660 +1-837-213-818\n", + "1 Danielle Mccann F 2001-07-10 459 Watts Path Lake Charles PA 42226 2367757570 \n", + "2 Gabriel Austin M 2002-05-15 59647 Bruce Gr New Alexander OR 48511 +1-834-046-299\n", + "4 Aaron Dixon M 2001-06-18 57205 Clark La Millermouth AZ 90284 001-593-504-97\n", + "11 Kevin Walker M 2000-01-16 12139 Smith Hi Coleshire OK 82926 001-227-524-14\n", + "12 Bradley Johnson M 2002-10-27 44057 Amber Cl Martinborough WA 96529 1246630797 \n", + "17 Charles Burns M 2000-03-21 374 David Fork West Tylervill AR 66484 180-517-6772x0\n", + "18 Dawn Phillips F 2004-08-30 9205 Margaret East James CA 35646 +1-986-030-376\n", + "20 Mikayla Thomas F 2003-10-03 3104 Riggs Rad Mathewsview TX 41272 772.655.0583 \n", + "22 Kristen Wiley F 2003-06-21 743 Linda Stre Christopherhav WI 29738 873-334-4318x1\n", + "25 Dr. Ashley F 2005-03-16 1004 Fuller Ro South Gary AR 14885 965.800.8174x1\n", + "29 Victoria Rodriguez F 2005-06-12 828 Lee Route Lake Felicia NC 44928 189.831.8711 \n", " ...\n", - " (Total: 200)" + " (Total: 96)" ] }, - "execution_count": 28, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -1499,7 +1502,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -1508,231 +1511,230 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\n", + " /* Show the tooltip text when you mouse over the tooltip container */\n", + " .djtooltip:hover .djtooltiptext {\n", + " visibility: visible;\n", + " }\n", + " \n", + " \n", + " \n", + "
\n", + "
\n", - "

student_id

\n", - " university-wide ID number\n", - "
\n", - "

first_name

\n", - " \n", - "
\n", - "

last_name

\n", - " \n", - "
\n", - "

sex

\n", - " \n", - "
\n", - "

date_of_birth

\n", - " \n", - "
\n", - "

home_address

\n", - " mailing street address\n", - "
\n", - "

home_city

\n", - " mailing address\n", - "
\n", - "

home_state

\n", - " US state acronym: e.g. OH\n", - "
\n", - "

home_zip

\n", - " zipcode e.g. 93979-4979\n", - "
\n", - "

home_phone

\n", - " e.g. 414.657.6883x0881\n", - "
5ErikaPhillips
\n", + " \n", + " \n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

date_of_birth

\n", + " \n", + "
\n", + "

home_address

\n", + " mailing street address\n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
\n", + "

home_zip

\n", + " zipcode e.g. 93979-4979\n", + "
\n", + "

home_phone

\n", + " e.g. 414.657.6883x0881\n", + "
0JillianFischerF1988-03-31911 Tiffany Pike Apt. 570ChristophertownUT56135(889)801-9551x4395
7NancyFox2000-12-1468627 Rodriguez Center Suite 032East AnthonyWY64660+1-837-213-8181x775
1DanielleMccannF2001-07-10459 Watts Path Suite 309Lake CharlesPA422262367757570
5MackenzieBoyerF1984-05-1696581 Jackson PortsLake IanmouthLA320465707101635
8DeborahVincent1988-12-269458 Ashley Stravenue Apt. 044Port MelissalandOH54499669-684-3236
8JacquelineColonF2001-01-10237 Elizabeth Pass Suite 178North Jennifer1996-02-2464635 Duncan PlaceSolisburghUT37335+1-696-997-1635x317
9StephanieKoch76301(983)027-5735x055
9NicoleLongF1993-06-128303 Tiffany RestWilsonviewMA14163001-917-947-1447x650
11DonnaWilcox1986-10-24804 Sarah Rue Suite 661Port KristinamouthND07732568-928-6900x28524
13AnnaGoldenF1990-05-25540 Alexis GreenEast RachaelviewMO329759502795098
12DanielleRoth1999-03-09177 Robles PortJamesmouthPA57615862.977.2190
14JessicaRamosF1991-01-04697 Stacy WallSchmidtboroughME35610069.269.5700x454
18ChristineNewmanF1999-02-2800473 Daniel Freeway Apt. 706New EdwardNM45981287-445-7199x999
19GabrielleCarr1996-08-138318 Kenneth LakesNorth MeganNJ61279(736)120-0323x9695
16HeidiLewisF2000-01-03097 Gina Junction Apt. 926New IsaacCT43134204.887.5818x900
21MonicaWilson1992-03-07810 Wanda Pines Suite 244NicholechesterWA39909001-467-054-8928x041
18DawnPhillipsF1993-11-136255 William DivideWest KathyviewNC65452632.749.3286x370
23KatieBrown2004-08-309205 Margaret Common Apt. 133East JamesCA35646+1-986-030-3766x9704
19BethNielsenF1985-03-299844 Joshua GardenSouth ElizabethMN92773(058)445-6195x48931
26TiffanyJohnson1989-10-1452211 Joseph CoursePort AmandabergWY77459001-289-285-4689
20MikaylaThomasF1997-09-252752 Valerie Passage Apt. 583KennedyshireNC03101255.083.4385
29CatherineCastro2003-10-033104 Riggs Radial Apt. 751MathewsviewTX41272772.655.0583
22KristenWileyF2000-05-08506 Sue FlatRobinsonmouthWY66233+1-820-511-6491
\n", - "

...

\n", - "

Total: 465

\n", - " " + "2003-06-21\n", + "743 Linda Street Suite 948\n", + "Christopherhaven\n", + "WI\n", + "29738\n", + "873-334-4318x117 \n", + " \n", + "

...

\n", + "

Total: 152

\n", + " " ], "text/plain": [ "*student_id first_name last_name sex date_of_birth home_address home_city home_state home_zip home_phone \n", "+------------+ +------------+ +-----------+ +-----+ +------------+ +------------+ +------------+ +------------+ +----------+ +------------+\n", - "5 Erika Phillips F 1988-03-31 911 Tiffany Pi Christophertow UT 56135 (889)801-9551x\n", - "7 Nancy Fox F 1984-05-16 96581 Jackson Lake Ianmouth LA 32046 5707101635 \n", - "8 Deborah Vincent F 2001-01-10 237 Elizabeth North Jennifer UT 37335 +1-696-997-163\n", - "9 Stephanie Koch F 1993-06-12 8303 Tiffany R Wilsonview MA 14163 001-917-947-14\n", - "11 Donna Wilcox F 1990-05-25 540 Alexis Gre East Rachaelvi MO 32975 9502795098 \n", - "12 Danielle Roth F 1991-01-04 697 Stacy Wall Schmidtborough ME 35610 069.269.5700x4\n", - "18 Christine Newman F 1999-02-28 00473 Daniel F New Edward NM 45981 287-445-7199x9\n", - "19 Gabrielle Carr F 2000-01-03 097 Gina Junct New Isaac CT 43134 204.887.5818x9\n", - "21 Monica Wilson F 1993-11-13 6255 William D West Kathyview NC 65452 632.749.3286x3\n", - "23 Katie Brown F 1985-03-29 9844 Joshua Ga South Elizabet MN 92773 (058)445-6195x\n", - "26 Tiffany Johnson F 1997-09-25 2752 Valerie P Kennedyshire NC 03101 255.083.4385 \n", - "29 Catherine Castro F 2000-05-08 506 Sue Flat Robinsonmouth WY 66233 +1-820-511-649\n", + "0 Jillian Fischer F 2000-12-14 68627 Rodrigue East Anthony WY 64660 +1-837-213-818\n", + "1 Danielle Mccann F 2001-07-10 459 Watts Path Lake Charles PA 42226 2367757570 \n", + "5 Mackenzie Boyer F 1988-12-26 9458 Ashley St Port Melissala OH 54499 669-684-3236 \n", + "8 Jacqueline Colon F 1996-02-24 64635 Duncan P Solisburgh UT 76301 (983)027-5735x\n", + "9 Nicole Long F 1986-10-24 804 Sarah Rue Port Kristinam ND 07732 568-928-6900x2\n", + "13 Anna Golden F 1999-03-09 177 Robles Por Jamesmouth PA 57615 862.977.2190 \n", + "14 Jessica Ramos F 1996-08-13 8318 Kenneth L North Megan NJ 61279 (736)120-0323x\n", + "16 Heidi Lewis F 1992-03-07 810 Wanda Pine Nicholechester WA 39909 001-467-054-89\n", + "18 Dawn Phillips F 2004-08-30 9205 Margaret East James CA 35646 +1-986-030-376\n", + "19 Beth Nielsen F 1989-10-14 52211 Joseph C Port Amandaber WY 77459 001-289-285-46\n", + "20 Mikayla Thomas F 2003-10-03 3104 Riggs Rad Mathewsview TX 41272 772.655.0583 \n", + "22 Kristen Wiley F 2003-06-21 743 Linda Stre Christopherhav WI 29738 873-334-4318x1\n", " ...\n", - " (Total: 465)" + " (Total: 152)" ] }, - "execution_count": 35, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -1750,43 +1752,1980 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([( 0, 'Jillian', 'Fischer', 'F', datetime.date(2000, 12, 14), '68627 Rodriguez Center Suite 032', 'East Anthony', 'WY', '64660', '+1-837-213-8181x775'),\n", + " ( 1, 'Danielle', 'Mccann', 'F', datetime.date(2001, 7, 10), '459 Watts Path Suite 309', 'Lake Charles', 'PA', '42226', '2367757570'),\n", + " ( 5, 'Mackenzie', 'Boyer', 'F', datetime.date(1988, 12, 26), '9458 Ashley Stravenue Apt. 044', 'Port Melissaland', 'OH', '54499', '669-684-3236'),\n", + " ( 8, 'Jacqueline', 'Colon', 'F', datetime.date(1996, 2, 24), '64635 Duncan Place', 'Solisburgh', 'UT', '76301', '(983)027-5735x055'),\n", + " ( 9, 'Nicole', 'Long', 'F', datetime.date(1986, 10, 24), '804 Sarah Rue Suite 661', 'Port Kristinamouth', 'ND', '07732', '568-928-6900x28524'),\n", + " ( 13, 'Anna', 'Golden', 'F', datetime.date(1999, 3, 9), '177 Robles Port', 'Jamesmouth', 'PA', '57615', '862.977.2190'),\n", + " ( 14, 'Jessica', 'Ramos', 'F', datetime.date(1996, 8, 13), '8318 Kenneth Lakes', 'North Megan', 'NJ', '61279', '(736)120-0323x9695'),\n", + " ( 16, 'Heidi', 'Lewis', 'F', datetime.date(1992, 3, 7), '810 Wanda Pines Suite 244', 'Nicholechester', 'WA', '39909', '001-467-054-8928x041'),\n", + " ( 18, 'Dawn', 'Phillips', 'F', datetime.date(2004, 8, 30), '9205 Margaret Common Apt. 133', 'East James', 'CA', '35646', '+1-986-030-3766x9704'),\n", + " ( 19, 'Beth', 'Nielsen', 'F', datetime.date(1989, 10, 14), '52211 Joseph Course', 'Port Amandaberg', 'WY', '77459', '001-289-285-4689'),\n", + " ( 20, 'Mikayla', 'Thomas', 'F', datetime.date(2003, 10, 3), '3104 Riggs Radial Apt. 751', 'Mathewsview', 'TX', '41272', '772.655.0583'),\n", + " ( 22, 'Kristen', 'Wiley', 'F', datetime.date(2003, 6, 21), '743 Linda Street Suite 948', 'Christopherhaven', 'WI', '29738', '873-334-4318x117'),\n", + " ( 24, 'Karen', 'Hall', 'F', datetime.date(1991, 1, 1), '802 Oconnor Shoal', 'Courtneyside', 'VA', '76699', '001-450-895-2937x557'),\n", + " ( 25, 'Dr.', 'Ashley', 'F', datetime.date(2005, 3, 16), '1004 Fuller Route Suite 401', 'South Gary', 'AR', '14885', '965.800.8174x14559'),\n", + " ( 27, 'Tara', 'Moore', 'F', datetime.date(1996, 9, 25), '09753 Timothy Plains Suite 918', 'South Brandichester', 'WV', '20935', '098-675-6040'),\n", + " ( 29, 'Victoria', 'Rodriguez', 'F', datetime.date(2005, 6, 12), '828 Lee Route', 'Lake Felicia', 'NC', '44928', '189.831.8711'),\n", + " ( 30, 'Debbie', 'Patterson', 'F', datetime.date(1994, 8, 29), '5977 Donald Hills Suite 426', 'North Jillian', 'VA', '86148', '573-589-7894'),\n", + " ( 31, 'Karen', 'Lee', 'F', datetime.date(2001, 5, 28), '92397 Smith Court Suite 681', 'New Robin', 'OR', '03775', '001-504-939-0893x736'),\n", + " ( 33, 'Kimberly', 'Carey', 'F', datetime.date(1998, 9, 7), '92403 Nancy Mills Apt. 662', 'Port Laurenside', 'NY', '99368', '(097)580-1867x2670'),\n", + " ( 34, 'Robin', 'Jones', 'F', datetime.date(1991, 4, 4), '872 Jackson Pines Suite 396', 'Youngborough', 'MA', '74118', '+1-092-142-9264x4959'),\n", + " ( 37, 'Heather', 'Humphrey', 'F', datetime.date(2000, 3, 12), '728 Amy Groves Apt. 457', 'South Alvin', 'IN', '27823', '4145328004'),\n", + " ( 38, 'Jasmine', 'Hicks', 'F', datetime.date(1986, 6, 22), '92100 Nunez Mountain Suite 341', 'New Rachaelside', 'DC', '53689', '5590657043'),\n", + " ( 39, 'Amanda', 'Wang', 'F', datetime.date(2002, 7, 9), '028 James Avenue', 'Donnaburgh', 'NV', '37215', '251-324-2667'),\n", + " ( 40, 'Amanda', 'Rosario', 'F', datetime.date(1999, 4, 10), '9563 Michael Pine Suite 931', 'Michaelshire', 'WV', '21208', '001-576-734-7225x242'),\n", + " ( 43, 'Amanda', 'Arroyo', 'F', datetime.date(1992, 4, 30), '49408 Williams Brook', 'Jennifermouth', 'NE', '68506', '126-624-6833'),\n", + " ( 45, 'Kelly', 'Wheeler', 'F', datetime.date(1999, 8, 28), '5199 Grace Stream', 'North Regina', 'ID', '42797', '(852)471-1509x6905'),\n", + " ( 48, 'Kimberly', 'Raymond', 'F', datetime.date(2004, 1, 28), '69277 Ray Key', 'South Danielletown', 'ID', '64803', '022.172.5672x603'),\n", + " ( 51, 'Deanna', 'Valdez', 'F', datetime.date(2001, 10, 20), '98307 Jennifer Track', 'South Johnmouth', 'FL', '54437', '788.587.3743'),\n", + " ( 53, 'Kristen', 'Jenkins', 'F', datetime.date(1989, 12, 6), '7196 Brian Center', 'Barbaraview', 'NE', '95825', '+1-816-931-7241x906'),\n", + " ( 54, 'Jessica', 'Bennett', 'F', datetime.date(2003, 8, 26), '615 Sharon Gateway', 'Port Sean', 'SC', '03709', '867.612.4267x9135'),\n", + " ( 55, 'Elizabeth', 'Howell', 'F', datetime.date(2002, 9, 4), '2352 Benjamin Gateway Suite 996', 'Josephburgh', 'IL', '50989', '001-719-428-3855'),\n", + " ( 56, 'Dr.', 'Bethany', 'F', datetime.date(2003, 9, 14), '074 Justin Spurs Apt. 653', 'West Davidview', 'WA', '58491', '839-843-6610'),\n", + " ( 59, 'Kristina', 'Kirby', 'F', datetime.date(1988, 5, 22), '89489 Young Way', 'Benjaminstad', 'ID', '20962', '+1-494-205-1237x9988'),\n", + " ( 61, 'Carolyn', 'Miller', 'F', datetime.date(2002, 9, 18), '955 Brandon Mill Suite 104', 'Tuckerton', 'IN', '83326', '308.665.7407'),\n", + " ( 64, 'Ashley', 'Young', 'F', datetime.date(2000, 2, 22), '36721 Simpson Lights Suite 035', 'Smithberg', 'OK', '84539', '733.287.7040'),\n", + " ( 66, 'Kristin', 'Mason', 'F', datetime.date(1987, 4, 14), '31397 Stone Parkway Suite 353', 'Lake Amanda', 'DC', '09007', '696.480.4255x92770'),\n", + " ( 67, 'Megan', 'Martinez', 'F', datetime.date(1994, 12, 22), '5276 Alexander Avenue Suite 928', 'North Rodney', 'KY', '62441', '(432)914-1079'),\n", + " ( 68, 'Janet', 'Anderson', 'F', datetime.date(2002, 6, 28), '206 Smith Viaduct Suite 699', 'Heiditon', 'SD', '81142', '529.788.9930'),\n", + " ( 69, 'Vanessa', 'Copeland', 'F', datetime.date(1995, 10, 28), '9695 Samuel Drive Suite 092', 'Christopherburgh', 'WI', '51730', '915-931-4672x9407'),\n", + " ( 70, 'Kathleen', 'Reed', 'F', datetime.date(1999, 8, 11), '41882 Allen Walk Apt. 899', 'Sandrabury', 'LA', '56451', '+1-106-887-8907x7524'),\n", + " ( 71, 'Katherine', 'Jenkins', 'F', datetime.date(1998, 12, 2), '51656 Joseph Rapid Apt. 522', 'New Richardville', 'KY', '63822', '+1-522-727-7641x7839'),\n", + " ( 74, 'Katherine', 'Potter', 'F', datetime.date(2005, 3, 17), '477 Carrillo Street', 'West Emily', 'RI', '62414', '164.047.7857'),\n", + " ( 75, 'Teresa', 'Carter', 'F', datetime.date(1996, 2, 26), '597 Aimee Locks', 'Johnfurt', 'NE', '45270', '408-191-1318x661'),\n", + " ( 77, 'Rachel', 'Bowen', 'F', datetime.date(2002, 5, 25), '547 Taylor Crest', 'Kellytown', 'ME', '61328', '707-544-3298x4719'),\n", + " ( 78, 'Emily', 'Jones', 'F', datetime.date(2001, 10, 18), '502 Cameron Estate', 'South Thomas', 'OK', '51464', '001-280-466-4043x825'),\n", + " ( 79, 'Jennifer', 'Nunez', 'F', datetime.date(1989, 3, 1), '158 James Green Apt. 156', 'West Jerry', 'CA', '77440', '(571)077-8320'),\n", + " ( 80, 'Jennifer', 'Nguyen', 'F', datetime.date(1993, 11, 10), '6937 Jennifer Lock Apt. 321', 'Deanstad', 'NY', '48675', '001-267-589-2918x447'),\n", + " ( 81, 'Jessica', 'Clay', 'F', datetime.date(1997, 5, 22), '536 Hernandez Walks Apt. 972', 'South Alanmouth', 'SD', '96555', '6697468738'),\n", + " ( 83, 'Kristin', 'Jones', 'F', datetime.date(1990, 4, 17), '10480 Walker Trail Apt. 498', 'New Robertland', 'RI', '01171', '+1-421-682-5503x016'),\n", + " ( 86, 'Andrea', 'Moreno', 'F', datetime.date(1990, 11, 24), '5411 Smith Ways', 'Sanchezberg', 'LA', '26114', '001-988-175-2362'),\n", + " ( 91, 'Sara', 'Huff', 'F', datetime.date(2005, 2, 20), '017 Rich Hills Suite 633', 'Brownchester', 'CT', '67581', '(933)377-7739x6876'),\n", + " ( 96, 'Kimberly', 'Butler', 'F', datetime.date(1994, 10, 13), '48400 Rodriguez Street', 'Lake Robertstad', 'KS', '77376', '6395340351'),\n", + " ( 97, 'Tracy', 'Baker', 'F', datetime.date(2001, 2, 1), '79211 Nathaniel Drive Suite 386', 'South Deannashire', 'IL', '28281', '+1-005-844-5959'),\n", + " (101, 'Andrea', 'Franklin', 'F', datetime.date(1996, 6, 9), '4446 Taylor Plaza', 'Port Davidside', 'AR', '93278', '493-991-1706x07012'),\n", + " (102, 'Alyssa', 'Brown', 'F', datetime.date(2004, 6, 9), '20243 Daniel Manor Apt. 402', 'Cynthiaberg', 'MN', '22079', '001-925-888-7154x436'),\n", + " (107, 'Brenda', 'Sanders', 'F', datetime.date(1990, 4, 25), '189 Stephen Knolls', 'Lake Amyborough', 'AR', '93809', '453.136.2198'),\n", + " (109, 'Deanna', 'Cox', 'F', datetime.date(2003, 9, 24), '164 Chapman Drive Apt. 274', 'South Robert', 'NC', '28885', '(235)873-8575'),\n", + " (111, 'Heather', 'Alvarado', 'F', datetime.date(2003, 2, 8), '042 Rebecca Mission', 'North Donna', 'MD', '87937', '803.854.9834'),\n", + " (116, 'Amber', 'Blankenship', 'F', datetime.date(1993, 12, 12), '41843 Jones Heights', 'Port Kara', 'CT', '78824', '+1-632-246-1077x531'),\n", + " (117, 'Kathryn', 'Davis', 'F', datetime.date(1994, 6, 26), '71512 Nicholas Rapids', 'Port Traciberg', 'MA', '84122', '001-011-104-2980x973'),\n", + " (118, 'Kristina', 'Norman', 'F', datetime.date(2005, 3, 4), '5665 Dennis Mill', 'Perkinschester', 'PA', '38683', '816-936-8115x18690'),\n", + " (119, 'Alicia', 'Sweeney', 'F', datetime.date(1996, 2, 6), '111 Mackenzie Junctions', 'West Linda', 'IN', '93938', '894-678-6968x11463'),\n", + " (122, 'Rebecca', 'Copeland', 'F', datetime.date(1994, 12, 13), '89462 Lewis Expressway Apt. 713', 'Haleymouth', 'NY', '78212', '+1-614-640-8342x007'),\n", + " (123, 'Anna', 'Cunningham', 'F', datetime.date(2000, 1, 23), '293 Cox Pine', 'Kruegerborough', 'TN', '94115', '095.131.3694'),\n", + " (125, 'Kylie', 'Diaz', 'F', datetime.date(2003, 8, 17), '4603 Douglas Fort Apt. 494', 'Colemanview', 'WI', '03325', '052.106.6014x95876'),\n", + " (126, 'Kristin', 'Yates', 'F', datetime.date(1989, 9, 26), '7297 Danielle Wells Apt. 787', 'Bruceview', 'AK', '01608', '471-379-8060'),\n", + " (127, 'Kayla', 'Sawyer', 'F', datetime.date(1987, 4, 1), '147 Peter Crest', 'Medinamouth', 'SC', '36063', '155.903.2519x32943'),\n", + " (128, 'Megan', 'Rodriguez', 'F', datetime.date(1990, 10, 23), '9126 Brown Plaza', 'New David', 'MD', '28416', '924-150-2547'),\n", + " (132, 'Deborah', 'Callahan', 'F', datetime.date(1992, 9, 21), '8147 Madison Wells', 'Moyerbury', 'MN', '55249', '815.014.3670'),\n", + " (133, 'Natalie', 'Thomas', 'F', datetime.date(2000, 4, 29), '18291 Marcus Lock Apt. 641', 'Carlosmouth', 'LA', '41866', '007.913.3881x94110'),\n", + " (137, 'Melanie', 'Miller', 'F', datetime.date(1994, 1, 4), '1921 Michael Courts', 'Thomasberg', 'TN', '95425', '+1-392-739-5934x213'),\n", + " (138, 'Amber', 'Peters', 'F', datetime.date(1995, 9, 4), '501 Burnett Inlet', 'West Brittany', 'VT', '08798', '853-889-0428x9721'),\n", + " (139, 'Kristen', 'Thomas', 'F', datetime.date(1993, 5, 31), '10941 Virginia Island', 'Penningtonhaven', 'IN', '79891', '(192)078-6665'),\n", + " (144, 'Samantha', 'Clark', 'F', datetime.date(1996, 6, 1), '9094 Owens Causeway Suite 106', 'Martinezland', 'WY', '49371', '975.309.8113x664'),\n", + " (148, 'Felicia', 'Vaughn', 'F', datetime.date(2001, 8, 5), '90380 Joshua Landing', 'New Steven', 'SD', '37352', '+1-918-046-4526x727'),\n", + " (149, 'Debra', 'West', 'F', datetime.date(2003, 11, 9), '49508 Kelly Springs', 'Hallland', 'MD', '01505', '(428)591-2046x3225'),\n", + " (151, 'Melissa', 'Thomas', 'F', datetime.date(2002, 6, 5), '454 Mike Walk Suite 188', 'Sarashire', 'IN', '93084', '(707)472-4151x17712'),\n", + " (154, 'Tanya', 'Williams', 'F', datetime.date(1985, 11, 19), '427 Ann Isle Apt. 757', 'West Kristinaville', 'CO', '45548', '001-440-604-4666x725'),\n", + " (155, 'Emily', 'Byrd', 'F', datetime.date(2004, 4, 3), '650 Roberts Road', 'West Zacharyfort', 'WY', '79775', '(443)399-6944'),\n", + " (157, 'Monica', 'Klein', 'F', datetime.date(1998, 12, 9), '53050 Garcia Street Apt. 359', 'Butlerstad', 'DE', '87791', '+1-510-661-1680'),\n", + " (158, 'Crystal', 'Leach', 'F', datetime.date(1991, 12, 17), '88760 Elizabeth Station', 'East Scott', 'IN', '76311', '717.446.6353x0889'),\n", + " (160, 'Maureen', 'Simon', 'F', datetime.date(1996, 10, 5), '9799 Julie Plaza Suite 583', 'Barryport', 'IN', '87386', '+1-365-486-0536x712'),\n", + " (161, 'Wendy', 'Mcclure', 'F', datetime.date(2005, 5, 13), '93627 Hansen Drives', 'New Lisa', 'KS', '01367', '968.733.9273x1230'),\n", + " (162, 'Veronica', 'Jackson', 'F', datetime.date(1987, 9, 10), '55017 Anna Viaduct', 'Michaelbury', 'MN', '76244', '(354)996-9659x86033'),\n", + " (164, 'Amy', 'Williams', 'F', datetime.date(1998, 7, 29), '334 Weeks Club Suite 643', 'Larsonborough', 'ME', '18462', '409-286-7863'),\n", + " (168, 'Alyssa', 'Guerra', 'F', datetime.date(1989, 12, 10), '5551 John Stream', 'West Erikafort', 'TX', '39968', '+1-384-619-7074'),\n", + " (169, 'Deborah', 'Cline', 'F', datetime.date(1996, 3, 3), '91196 Anthony Wells', 'Brownhaven', 'MT', '23238', '(671)274-4744x719'),\n", + " (171, 'Diana', 'Wagner', 'F', datetime.date(2005, 10, 21), '957 Wong Lights', 'East Matthew', 'MA', '01000', '(282)563-2014x87937'),\n", + " (172, 'Tracy', 'Patton', 'F', datetime.date(2005, 4, 6), '82521 Brooks Tunnel Apt. 807', 'West Richardhaven', 'NH', '27416', '856.087.2857'),\n", + " (173, 'Taylor', 'Fleming', 'F', datetime.date(1992, 12, 8), '96752 Villa Stream Apt. 607', 'North Sarahchester', 'LA', '55882', '642-759-4869x1650'),\n", + " (175, 'Melissa', 'Keller', 'F', datetime.date(1994, 8, 23), '2874 Saunders Glen Suite 317', 'Denisebury', 'TN', '65422', '(484)124-9158x3337'),\n", + " (176, 'Crystal', 'Short', 'F', datetime.date(1996, 10, 11), '14918 Brown Walk', 'West Jeff', 'IN', '19394', '001-611-275-1179x645'),\n", + " (182, 'Katie', 'Palmer', 'F', datetime.date(1989, 8, 24), '439 Wright Prairie Suite 796', 'Fullerborough', 'ME', '03583', '+1-438-161-7483x788'),\n", + " (184, 'Jordan', 'Jones', 'F', datetime.date(1993, 8, 29), '227 Christy Causeway Suite 269', 'Port Brandonchester', 'CT', '34678', '(152)951-9193x444'),\n", + " (187, 'Kimberly', 'Anthony', 'F', datetime.date(1996, 1, 15), '71203 Ray Spur', 'Torrestown', 'KY', '09573', '(786)320-7649x2637'),\n", + " (188, 'Ashley', 'Guzman', 'F', datetime.date(2001, 11, 14), '011 Fowler Drive Apt. 620', 'Garciamouth', 'MI', '94068', '001-809-948-0078x762'),\n", + " (189, 'Kelly', 'Thompson', 'F', datetime.date(2004, 4, 26), '33144 Jeffery Courts Suite 171', 'North Isaiah', 'OR', '50923', '001-437-662-7268x603'),\n", + " (192, 'Margaret', 'Hughes', 'F', datetime.date(1986, 11, 17), '4879 Michelle Land Suite 673', 'Jenniferstad', 'PA', '27286', '001-390-553-2632'),\n", + " (193, 'Lisa', 'Escobar', 'F', datetime.date(1998, 3, 13), '926 Stark Crossing', 'South Bryan', 'MS', '86227', '(331)589-7870x58178'),\n", + " (198, 'Sarah', 'Odonnell', 'F', datetime.date(1989, 9, 13), '18158 Ellison Hills Apt. 244', 'Travisfurt', 'AL', '04641', '(792)274-1066x26038'),\n", + " (199, 'Stephanie', 'Curtis', 'F', datetime.date(1995, 11, 14), '133 Christopher Junction Apt. 134', 'South Jennifer', 'CA', '62643', '614-287-6200x23188'),\n", + " (200, 'Kimberly', 'Taylor', 'F', datetime.date(1987, 8, 14), '42166 Flores Island', 'Roberthaven', 'MT', '17048', '+1-689-653-0999'),\n", + " (201, 'Lacey', 'Taylor', 'F', datetime.date(1988, 4, 26), '97005 Lisa Mill Suite 678', 'Newtonshire', 'TN', '69198', '+1-357-502-7339x1514'),\n", + " (202, 'Melissa', 'Little', 'F', datetime.date(1999, 7, 13), '383 Haynes Court', 'North Lindsey', 'NY', '29588', '024.540.9045'),\n", + " (206, 'Karen', 'Bryan', 'F', datetime.date(1999, 6, 13), '3670 Carol Village Suite 907', 'South Christinaville', 'GA', '37283', '001-359-306-4754x614'),\n", + " (207, 'Anita', 'Harmon', 'F', datetime.date(1995, 6, 27), '9485 Werner Underpass', 'Jeffreyview', 'WV', '39997', '064.111.8438'),\n", + " (208, 'Beth', 'Smith', 'F', datetime.date(2004, 9, 30), '958 Kimberly Forges', 'Newmanland', 'NE', '78284', '492.071.6510'),\n", + " (209, 'Carrie', 'Dennis', 'F', datetime.date(1993, 11, 12), '3666 Adams Vista', 'North Jenniferland', 'SD', '02830', '+1-395-365-9523x6997'),\n", + " (210, 'Paula', 'Perez', 'F', datetime.date(2000, 8, 15), '17911 Frazier Roads', 'Port Tasha', 'UT', '21725', '405.906.1669'),\n", + " (213, 'Brianna', 'Rose', 'F', datetime.date(1998, 3, 31), '0428 Gomez Trail Apt. 639', 'North Donna', 'WA', '26718', '001-823-285-7191x368'),\n", + " (220, 'Kathryn', 'Lopez', 'F', datetime.date(1988, 3, 25), '861 Tanya Knolls', 'Payneside', 'NJ', '90616', '(074)095-3943x06918'),\n", + " (221, 'Sarah', 'Myers', 'F', datetime.date(1993, 1, 26), '34262 Christopher Street', 'Cabrerabury', 'HI', '26895', '915.044.0595'),\n", + " (224, 'Donna', 'Smith', 'F', datetime.date(2004, 12, 19), '804 Mccoy Turnpike Suite 847', 'Bautistafurt', 'WI', '29114', '(046)527-9112x706'),\n", + " (226, 'Shelley', 'Brooks', 'F', datetime.date(2005, 9, 27), '31828 Taylor Glen Suite 101', 'New Jenniferhaven', 'NJ', '44670', '+1-491-708-0414x864'),\n", + " (228, 'Andrea', 'Barrett', 'F', datetime.date(1993, 8, 11), '7100 Scott Lake', 'Kristenstad', 'OK', '09274', '001-500-223-8945x457'),\n", + " (234, 'Katelyn', 'Hoover', 'F', datetime.date(1997, 12, 24), '9315 Christopher Road', 'Loriburgh', 'CA', '75454', '275.704.7134x661'),\n", + " (235, 'Shannon', 'Williams', 'F', datetime.date(2002, 7, 8), '08663 Kimberly Expressway Apt. 575', 'South Wyatt', 'AL', '80993', '314-058-0010'),\n", + " (239, 'Yvette', 'Galloway', 'F', datetime.date(2002, 7, 24), '12076 Anthony Square', 'Samueltown', 'NY', '34260', '(878)866-9801x91611'),\n", + " (241, 'Sandra', 'Winters', 'F', datetime.date(2005, 7, 31), '907 Mario Lodge Apt. 942', 'Shermanville', 'ND', '45545', '+1-753-365-4045'),\n", + " (248, 'Richard', 'Noble', 'M', datetime.date(2003, 2, 5), '7977 Adams Crossroad Suite 214', 'Port Catherineland', 'UT', '34911', '458.117.5719'),\n", + " (249, 'Ashley', 'Thomas', 'F', datetime.date(1996, 8, 29), '241 Katie Fort Apt. 087', 'New Ebony', 'ME', '26233', '135-652-0859x3049'),\n", + " (251, 'Ashley', 'Miller', 'F', datetime.date(1988, 6, 13), '1805 Alexis Villages', 'Morrisonton', 'MD', '93116', '(263)336-5912'),\n", + " (254, 'Deanna', 'Price', 'F', datetime.date(1994, 11, 4), '120 Edwards Expressway Suite 511', 'Kathleenberg', 'CA', '19876', '001-940-447-1052x158'),\n", + " (258, 'Julie', 'Jones', 'F', datetime.date(2005, 8, 2), '35304 Rachel Meadows Suite 288', 'North Jeffreyberg', 'AZ', '48886', '141-934-7302x6340'),\n", + " (259, 'Monica', 'Hill', 'F', datetime.date(1992, 10, 18), '945 Angela Key', 'Johnchester', 'GA', '23266', '0750320331'),\n", + " (260, 'Joanne', 'Williams', 'F', datetime.date(2001, 2, 7), '85254 Janet Pines', 'Lake Daniel', 'AZ', '34031', '623.410.2275'),\n", + " (262, 'Debra', 'Riggs', 'F', datetime.date(2002, 5, 3), '090 Andrew Mountains Apt. 054', 'New Amandachester', 'WA', '61679', '001-635-536-1950x612'),\n", + " (264, 'Heidi', 'Charles', 'F', datetime.date(1990, 3, 15), '927 Chandler Groves Suite 929', 'Leeville', 'MA', '27167', '(160)618-6264x340'),\n", + " (266, 'Valerie', 'Smith', 'F', datetime.date(1989, 1, 22), '4709 Patrick Wall', 'West Ericview', 'AL', '73935', '(783)138-7164'),\n", + " (268, 'Debra', 'Schmitt', 'F', datetime.date(1992, 12, 22), '74229 James Hills Apt. 780', 'East Kristina', 'IN', '43119', '516.311.9867'),\n", + " (269, 'Veronica', 'Anderson', 'F', datetime.date(1997, 5, 19), '0043 Baker Springs', 'Kristinaton', 'UT', '04908', '(811)585-2197x666'),\n", + " (271, 'Elizabeth', 'Brock', 'F', datetime.date(1987, 3, 6), '8230 Mitchell Extension Apt. 275', 'Port Anthony', 'CA', '31032', '190-577-0466x2936'),\n", + " (273, 'Brenda', 'Lynch', 'F', datetime.date(2002, 9, 10), '11630 Jeremy Mountains', 'Austinchester', 'SC', '45091', '(460)652-5911x142'),\n", + " (275, 'Diane', 'Walker', 'F', datetime.date(2002, 6, 22), '929 Olivia Turnpike', 'Lake Keithport', 'DE', '11107', '1672377368'),\n", + " (276, 'Sara', 'Murphy', 'F', datetime.date(1987, 7, 5), '178 Smith Prairie', 'West Allison', 'ND', '93840', '610.384.6473x01662'),\n", + " (278, 'Shannon', 'Mayer', 'F', datetime.date(1997, 4, 10), '8730 Derrick Mews Suite 666', 'Craneborough', 'NV', '24146', '001-006-189-0389x612'),\n", + " (279, 'Crystal', 'Parker', 'F', datetime.date(2001, 11, 28), '1314 Jessica Causeway', 'Port Stephen', 'GA', '98006', '(321)139-5367'),\n", + " (280, 'Tamara', 'Walter', 'F', datetime.date(2001, 8, 18), '136 Garrison Prairie Apt. 775', 'South Sara', 'IN', '72450', '+1-823-985-9579x9827'),\n", + " (281, 'Amber', 'Benson', 'F', datetime.date(1988, 3, 21), '90660 Hamilton Orchard Apt. 932', 'Matthewstown', 'OK', '20573', '001-513-126-8556x683'),\n", + " (282, 'Colleen', 'Barnes', 'F', datetime.date(1992, 1, 19), '959 Lin Common Apt. 811', 'Port Phillip', 'MO', '72239', '341.284.4253x580'),\n", + " (283, 'Paula', 'Clark', 'F', datetime.date(2005, 9, 30), '49434 Robert Shore', 'Ortizport', 'MI', '20527', '664-842-9310'),\n", + " (284, 'Jennifer', 'Cole', 'F', datetime.date(1986, 11, 18), '821 Kline Valleys Apt. 697', 'South Jamesfort', 'WI', '57201', '8222564998'),\n", + " (286, 'Angela', 'Hill', 'F', datetime.date(2000, 7, 2), '126 Collins Neck', 'Seanfurt', 'CT', '98022', '213-109-1553x39625'),\n", + " (287, 'Mrs.', 'Lisa', 'F', datetime.date(1994, 10, 16), '2117 Walls Harbor Suite 967', 'Lake Anthony', 'WI', '41121', '+1-886-620-3505'),\n", + " (288, 'Olivia', 'Martinez', 'F', datetime.date(2001, 6, 25), '0897 Brian Union', 'Fosterfurt', 'ND', '42558', '8320317842'),\n", + " (289, 'Alyssa', 'Oliver', 'F', datetime.date(1999, 7, 30), '979 Daisy Cliffs', 'Maureenside', 'NY', '41650', '(927)504-5856'),\n", + " (290, 'Barbara', 'Hicks', 'F', datetime.date(2003, 2, 14), '06525 Coleman Via', 'Port Vanessatown', 'DC', '80068', '(403)892-5401x1797'),\n", + " (291, 'Shari', 'Rodriguez', 'F', datetime.date(2002, 4, 7), '14993 Reeves Cove', 'East Frederick', 'MN', '14832', '436.215.4569x041'),\n", + " (292, 'Wendy', 'Mathews', 'F', datetime.date(2003, 12, 13), '8728 Jamie Courts', 'New Leefort', 'AL', '66380', '+1-716-103-4197x654'),\n", + " (296, 'Michelle', 'George', 'F', datetime.date(1994, 1, 2), '56285 Stephanie Wall Suite 800', 'Vazquezside', 'AR', '02142', '(683)009-1622x26181'),\n", + " (298, 'Christine', 'Johnson', 'F', datetime.date(2003, 2, 14), '4725 Petersen Via', 'Clarkburgh', 'NC', '75437', '+1-281-129-5589x225'),\n", + " (299, 'Lisa', 'Sheppard', 'F', datetime.date(2004, 4, 2), '95923 Reynolds Fork Suite 719', 'West Laura', 'LA', '23546', '001-842-369-3244x325')],\n", + " dtype=[('student_id', '\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
first_namelast_namesexdate_of_birthhome_addresshome_cityhome_statehome_ziphome_phone
student_id
0JillianFischerF2000-12-1468627 Rodriguez Center Suite 032East AnthonyWY64660+1-837-213-8181x775
1DanielleMccannF2001-07-10459 Watts Path Suite 309Lake CharlesPA422262367757570
5MackenzieBoyerF1988-12-269458 Ashley Stravenue Apt. 044Port MelissalandOH54499669-684-3236
8JacquelineColonF1996-02-2464635 Duncan PlaceSolisburghUT76301(983)027-5735x055
9NicoleLongF1986-10-24804 Sarah Rue Suite 661Port KristinamouthND07732568-928-6900x28524
..............................
291ShariRodriguezF2002-04-0714993 Reeves CoveEast FrederickMN14832436.215.4569x041
292WendyMathewsF2003-12-138728 Jamie CourtsNew LeefortAL66380+1-716-103-4197x654
296MichelleGeorgeF1994-01-0256285 Stephanie Wall Suite 800VazquezsideAR02142(683)009-1622x26181
298ChristineJohnsonF2003-02-144725 Petersen ViaClarkburghNC75437+1-281-129-5589x225
299LisaSheppardF2004-04-0295923 Reynolds Fork Suite 719West LauraLA23546001-842-369-3244x325
\n", + "

152 rows × 9 columns

\n", + "" + ], + "text/plain": [ + " first_name last_name sex date_of_birth \\\n", + "student_id \n", + "0 Jillian Fischer F 2000-12-14 \n", + "1 Danielle Mccann F 2001-07-10 \n", + "5 Mackenzie Boyer F 1988-12-26 \n", + "8 Jacqueline Colon F 1996-02-24 \n", + "9 Nicole Long F 1986-10-24 \n", + "... ... ... .. ... \n", + "291 Shari Rodriguez F 2002-04-07 \n", + "292 Wendy Mathews F 2003-12-13 \n", + "296 Michelle George F 1994-01-02 \n", + "298 Christine Johnson F 2003-02-14 \n", + "299 Lisa Sheppard F 2004-04-02 \n", + "\n", + " home_address home_city home_state \\\n", + "student_id \n", + "0 68627 Rodriguez Center Suite 032 East Anthony WY \n", + "1 459 Watts Path Suite 309 Lake Charles PA \n", + "5 9458 Ashley Stravenue Apt. 044 Port Melissaland OH \n", + "8 64635 Duncan Place Solisburgh UT \n", + "9 804 Sarah Rue Suite 661 Port Kristinamouth ND \n", + "... ... ... ... \n", + "291 14993 Reeves Cove East Frederick MN \n", + "292 8728 Jamie Courts New Leefort AL \n", + "296 56285 Stephanie Wall Suite 800 Vazquezside AR \n", + "298 4725 Petersen Via Clarkburgh NC \n", + "299 95923 Reynolds Fork Suite 719 West Laura LA \n", + "\n", + " home_zip home_phone \n", + "student_id \n", + "0 64660 +1-837-213-8181x775 \n", + "1 42226 2367757570 \n", + "5 54499 669-684-3236 \n", + "8 76301 (983)027-5735x055 \n", + "9 07732 568-928-6900x28524 \n", + "... ... ... \n", + "291 14832 436.215.4569x041 \n", + "292 66380 +1-716-103-4197x654 \n", + "296 02142 (683)009-1622x26181 \n", + "298 75437 +1-281-129-5589x225 \n", + "299 23546 001-842-369-3244x325 \n", + "\n", + "[152 rows x 9 columns]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "female_texans.fetch(format=\"frame\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "152" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(female_texans)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -1795,7 +3734,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -1804,39 +3743,485 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

date_of_birth

\n", + " \n", + "
\n", + "

home_address

\n", + " mailing street address\n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
\n", + "

home_zip

\n", + " zipcode e.g. 93979-4979\n", + "
\n", + "

home_phone

\n", + " e.g. 414.657.6883x0881\n", + "
8JacquelineColonF1996-02-2464635 Duncan PlaceSolisburghUT76301(983)027-5735x055
210PaulaPerezF2000-08-1517911 Frazier RoadsPort TashaUT21725405.906.1669
248RichardNobleM2003-02-057977 Adams Crossroad Suite 214Port CatherinelandUT34911458.117.5719
269VeronicaAndersonF1997-05-190043 Baker SpringsKristinatonUT04908(811)585-2197x666
\n", + " \n", + "

Total: 4

\n", + " " + ], + "text/plain": [ + "*student_id first_name last_name sex date_of_birth home_address home_city home_state home_zip home_phone \n", + "+------------+ +------------+ +-----------+ +-----+ +------------+ +------------+ +------------+ +------------+ +----------+ +------------+\n", + "8 Jacqueline Colon F 1996-02-24 64635 Duncan P Solisburgh UT 76301 (983)027-5735x\n", + "210 Paula Perez F 2000-08-15 17911 Frazier Port Tasha UT 21725 405.906.1669 \n", + "248 Richard Noble M 2003-02-05 7977 Adams Cro Port Catherine UT 34911 458.117.5719 \n", + "269 Veronica Anderson F 1997-05-19 0043 Baker Spr Kristinaton UT 04908 (811)585-2197x\n", + " (Total: 4)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "utah_genz" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

first_name

\n", + " \n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

date_of_birth

\n", + " \n", + "
\n", + "

home_address

\n", + " mailing street address\n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
\n", + "

home_zip

\n", + " zipcode e.g. 93979-4979\n", + "
\n", + "

home_phone

\n", + " e.g. 414.657.6883x0881\n", + "
8JacquelineColonF1996-02-2464635 Duncan PlaceSolisburghUT76301(983)027-5735x055
210PaulaPerezF2000-08-1517911 Frazier RoadsPort TashaUT21725405.906.1669
248RichardNobleM2003-02-057977 Adams Crossroad Suite 214Port CatherinelandUT34911458.117.5719
269VeronicaAndersonF1997-05-190043 Baker SpringsKristinatonUT04908(811)585-2197x666
\n", + " \n", + "

Total: 4

\n", + " " + ], + "text/plain": [ + "*student_id first_name last_name sex date_of_birth home_address home_city home_state home_zip home_phone \n", + "+------------+ +------------+ +-----------+ +-----+ +------------+ +------------+ +------------+ +------------+ +----------+ +------------+\n", + "8 Jacqueline Colon F 1996-02-24 64635 Duncan P Solisburgh UT 76301 (983)027-5735x\n", + "210 Paula Perez F 2000-08-15 17911 Frazier Port Tasha UT 21725 405.906.1669 \n", + "248 Richard Noble M 2003-02-05 7977 Adams Cro Port Catherine UT 34911 458.117.5719 \n", + "269 Veronica Anderson F 1997-05-19 0043 Baker Spr Kristinaton UT 04908 (811)585-2197x\n", + " (Total: 4)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "utah_genz = utah_genz.proj('last_name', 'home_city','home_state','sex')\n", "utah_genz" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'student',\n", + " 'engine': 'InnoDB',\n", + " 'version': 10,\n", + " 'row_format': 'Dynamic',\n", + " 'rows': 0,\n", + " 'avg_row_length': 0,\n", + " 'data_length': 16384,\n", + " 'max_data_length': 0,\n", + " 'index_length': 0,\n", + " 'data_free': 0,\n", + " 'auto_increment': None,\n", + " 'create_time': datetime.datetime(2020, 10, 27, 9, 45, 43),\n", + " 'update_time': None,\n", + " 'check_time': None,\n", + " 'collation': 'latin1_swedish_ci',\n", + " 'checksum': None,\n", + " 'create_options': '',\n", + " 'comment': ''}" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "for r in utah_genz:\n", - " print(r)" + "utah_genz.heading.table_status" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

student_id

\n", + " university-wide ID number\n", + "
\n", + "

last_name

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
\n", + "

home_city

\n", + " mailing address\n", + "
\n", + "

home_state

\n", + " US state acronym: e.g. OH\n", + "
8ColonFSolisburghUT
210PerezFPort TashaUT
248NobleMPort CatherinelandUT
269AndersonFKristinatonUT
\n", + " \n", + "

Total: 4

\n", + " " + ], + "text/plain": [ + "*student_id last_name sex home_city home_state \n", + "+------------+ +-----------+ +-----+ +------------+ +------------+\n", + "8 Colon F Solisburgh UT \n", + "210 Perez F Port Tasha UT \n", + "248 Noble M Port Catherine UT \n", + "269 Anderson F Kristinaton UT \n", + " (Total: 4)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utah_genz = utah_genz.proj('last_name', 'home_city','home_state','sex')\n", + "utah_genz" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['student_id', 'last_name', 'sex', 'home_city', 'home_state']" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utah_genz.heading.non_blobs" + ] } ], "metadata": { @@ -1855,7 +4240,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.8.5" } }, "nbformat": 4, diff --git a/notebooks/Improvements.ipynb b/notebooks/Improvements.ipynb new file mode 100644 index 0000000..8c52e69 --- /dev/null +++ b/notebooks/Improvements.ipynb @@ -0,0 +1,1459 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Some improvements of queries from an IBL query notebook" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['ibl_acquisition',\n", + " 'ibl_action',\n", + " 'ibl_alyxraw',\n", + " 'ibl_analyses_behavior',\n", + " 'ibl_behavior',\n", + " 'ibl_data',\n", + " 'ibl_dj_acquisition',\n", + " 'ibl_dj_action',\n", + " 'ibl_dj_alyxraw',\n", + " 'ibl_dj_analyses_behavior',\n", + " 'ibl_dj_behavior',\n", + " 'ibl_dj_data',\n", + " 'ibl_dj_ingest_acquisition',\n", + " 'ibl_dj_ingest_action',\n", + " 'ibl_dj_ingest_data',\n", + " 'ibl_dj_ingest_reference',\n", + " 'ibl_dj_ingest_subject',\n", + " 'ibl_dj_plotting_behavior',\n", + " 'ibl_dj_reference',\n", + " 'ibl_dj_subject',\n", + " 'ibl_ephys',\n", + " 'ibl_ingest_acquisition',\n", + " 'ibl_ingest_action',\n", + " 'ibl_ingest_data',\n", + " 'ibl_ingest_reference',\n", + " 'ibl_ingest_subject',\n", + " 'ibl_plotting_behavior',\n", + " 'ibl_reference',\n", + " 'ibl_subject']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.list_schemas()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "reference = dj.create_virtual_module('reference', 'ibl_dj_reference')\n", + "subject = dj.create_virtual_module('subject', 'ibl_dj_subject')\n", + "action = dj.create_virtual_module('action', 'ibl_dj_action')\n", + "acquisition = dj.create_virtual_module('acquisition', 'ibl_dj_acquisition')\n", + "data = dj.create_virtual_module('data', 'ibl_dj_data')\n", + "behavior = dj.create_virtual_module('behavior', 'ibl_dj_behavior')" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "176\n", + "\n", + "176\n", + "\n", + "\n", + "\n", + "subject.Litter\n", + "\n", + "\n", + "subject.Litter\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "176->subject.Litter\n", + "\n", + "\n", + "\n", + "\n", + "178\n", + "\n", + "178\n", + "\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "subject.Subject\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "178->subject.Subject\n", + "\n", + "\n", + "\n", + "\n", + "171\n", + "\n", + "171\n", + "\n", + "\n", + "\n", + "subject.BreedingPair\n", + "\n", + "\n", + "subject.BreedingPair\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "171->subject.BreedingPair\n", + "\n", + "\n", + "\n", + "\n", + "172\n", + "\n", + "172\n", + "\n", + "\n", + "\n", + "172->subject.BreedingPair\n", + "\n", + "\n", + "\n", + "\n", + "177\n", + "\n", + "177\n", + "\n", + "\n", + "\n", + "177->subject.Subject\n", + "\n", + "\n", + "\n", + "\n", + "173\n", + "\n", + "173\n", + "\n", + "\n", + "\n", + "173->subject.BreedingPair\n", + "\n", + "\n", + "\n", + "\n", + "174\n", + "\n", + "174\n", + "\n", + "\n", + "\n", + "174->subject.BreedingPair\n", + "\n", + "\n", + "\n", + "\n", + "subject.SubjectUser\n", + "\n", + "\n", + "subject.SubjectUser\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->172\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->173\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->174\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.SubjectUser\n", + "\n", + "\n", + "\n", + "\n", + "subject.Death\n", + "\n", + "\n", + "subject.Death\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.Death\n", + "\n", + "\n", + "\n", + "\n", + "subject.SubjectProject\n", + "\n", + "\n", + "subject.SubjectProject\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.SubjectProject\n", + "\n", + "\n", + "\n", + "\n", + "subject.Weaning\n", + "\n", + "\n", + "subject.Weaning\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.Weaning\n", + "\n", + "\n", + "\n", + "\n", + "subject.Caging\n", + "\n", + "\n", + "subject.Caging\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.Caging\n", + "\n", + "\n", + "\n", + "\n", + "subject.UserHistory\n", + "\n", + "\n", + "subject.UserHistory\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.UserHistory\n", + "\n", + "\n", + "\n", + "\n", + "subject.GenotypeTest\n", + "\n", + "\n", + "subject.GenotypeTest\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.GenotypeTest\n", + "\n", + "\n", + "\n", + "\n", + "subject.SubjectLab\n", + "\n", + "\n", + "subject.SubjectLab\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.SubjectLab\n", + "\n", + "\n", + "\n", + "\n", + "subject.Implant\n", + "\n", + "\n", + "subject.Implant\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.Implant\n", + "\n", + "\n", + "\n", + "\n", + "subject.LitterSubject\n", + "\n", + "\n", + "subject.LitterSubject\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.LitterSubject\n", + "\n", + "\n", + "\n", + "\n", + "subject.Zygosity\n", + "\n", + "\n", + "subject.Zygosity\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Subject->subject.Zygosity\n", + "\n", + "\n", + "\n", + "\n", + "subject.Species\n", + "\n", + "\n", + "subject.Species\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Line\n", + "\n", + "\n", + "subject.Line\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Species->subject.Line\n", + "\n", + "\n", + "\n", + "\n", + "subject.LineAllele\n", + "\n", + "\n", + "subject.LineAllele\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Sequence\n", + "\n", + "\n", + "subject.Sequence\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.AlleleSequence\n", + "\n", + "\n", + "subject.AlleleSequence\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Sequence->subject.AlleleSequence\n", + "\n", + "\n", + "\n", + "\n", + "subject.Sequence->subject.GenotypeTest\n", + "\n", + "\n", + "\n", + "\n", + "subject.Strain\n", + "\n", + "\n", + "subject.Strain\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Strain->subject.Line\n", + "\n", + "\n", + "\n", + "\n", + "subject.BreedingPair->subject.Litter\n", + "\n", + "\n", + "\n", + "\n", + "subject.Allele\n", + "\n", + "\n", + "subject.Allele\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Allele->subject.LineAllele\n", + "\n", + "\n", + "\n", + "\n", + "subject.Allele->subject.AlleleSequence\n", + "\n", + "\n", + "\n", + "\n", + "subject.Allele->subject.Zygosity\n", + "\n", + "\n", + "\n", + "\n", + "subject.Litter->subject.LitterSubject\n", + "\n", + "\n", + "\n", + "\n", + "subject.Source\n", + "\n", + "\n", + "subject.Source\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "subject.Source->178\n", + "\n", + "\n", + "\n", + "\n", + "subject.Source->subject.Allele\n", + "\n", + "\n", + "\n", + "\n", + "subject.Line->176\n", + "\n", + "\n", + "\n", + "\n", + "subject.Line->171\n", + "\n", + "\n", + "\n", + "\n", + "subject.Line->177\n", + "\n", + "\n", + "\n", + "\n", + "subject.Line->subject.LineAllele\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(subject)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ORIGINAL: \n", + "```python\n", + "# FIND THE DATA WE NEED\n", + "# FIRST, WHICH SUBJECTS ARE DOING THIS CA EXPERIMENT?\n", + "subj = (subject.Subject() - subject.Death & 'subject_birth_date < \"2018-09-01\"').proj('subject_nickname', 'sex') * \\\n", + " (subject.SubjectLab() & 'lab_name=\"churchlandlab\"').proj()\n", + "print(subj)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject_uuid

\n", + " \n", + "
\n", + "

subject_nickname

\n", + " nickname\n", + "
\n", + "

sex

\n", + " sex\n", + "
034c07c5-69b0-48c7-ab3e-e491e4dbb725IBL_25M
1208c089-8b8e-4a87-98f0-05a68fb18370IBL_13M
278bf922-073e-4ef4-90b5-ccf4e25e08feIBL_26M
3e97e1d3-2a0f-44e5-b63f-36196d78457aIBL_34M
3f854f88-7879-4368-9e0d-41edea3bfab9IBL_11M
52a800fc-cbbc-45e9-97b1-ad6f6166e9afIBL_1M
55381f61-4e47-4baa-beb9-70068c0ad62cIBL_46M
56b81bb0-fffb-4875-9cd6-29465782fb0eIBL_24M
64ff6fea-89a5-449d-a9f4-53f4b200e7dbIBL_20M
6af88109-8b3d-485c-8486-f8b35d2bf061IBL_23M
7c751b49-55a6-4eac-9bdb-367faf2a18eeIBL_10M
a7ccdd67-8443-458a-a0ba-2f76a826a7a7IBL_21M
\n", + "

...

\n", + "

Total: 15

\n", + " " + ], + "text/plain": [ + "*subject_uuid subject_nickna sex \n", + "+------------+ +------------+ +-----+\n", + "034c07c5-69b0- IBL_25 M \n", + "1208c089-8b8e- IBL_13 M \n", + "278bf922-073e- IBL_26 M \n", + "3e97e1d3-2a0f- IBL_34 M \n", + "3f854f88-7879- IBL_11 M \n", + "52a800fc-cbbc- IBL_1 M \n", + "55381f61-4e47- IBL_46 M \n", + "56b81bb0-fffb- IBL_24 M \n", + "64ff6fea-89a5- IBL_20 M \n", + "6af88109-8b3d- IBL_23 M \n", + "7c751b49-55a6- IBL_10 M \n", + "a7ccdd67-8443- IBL_21 M \n", + " ...\n", + " (Total: 15)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# IMPROVED: \n", + "subj = (subject.Subject * subject.SubjectLab - subject.Death & 'subject_birth_date < \"2018-09-01\"' & \n", + " {'lab_name': 'churchlandlab'}).proj('subject_nickname', 'sex')\n", + "subj" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ORIGINAL \n", + "\n", + "```python\n", + "# get date for each weighing\n", + "weight_with_date = action.Weighing.proj('weight', session_date='DATE(weighing_time)')\n", + "# create a table with primary key to be the combination of subject_uuid and session_date\n", + "# dj.U, U means uniform, all possible combinations of subject uuid and session_date, when\n", + "# restricted with weight_with_date, it returns all existing combinations of subject_uuid and \n", + "# session_date in the table weight_with_date\n", + "# Note that there are more entries in weight_with_date than in weight_date, indicating there\n", + "# exists more than one weighing for some dates.\n", + "weight_date = (dj.U('subject_uuid', 'session_date') & weight_with_date)\n", + "\n", + "\n", + "# Aggregation to get average weight for each date\n", + "# before .aggr is the table you want aggregate, basically you get one value for each entry in \n", + "# weight_with_date\n", + "# first argument is the table that is useful to compute the value you need, here weight_with_date\n", + "# provides all weights for each date, 'weight' is an attribute in the table weight_with_date\n", + "# note that the results have the same number of entries as weight_date\n", + "avg_weight_date = weight_date.aggr(weight_with_date, avg_weight='AVG(weight)')\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject_uuid

\n", + " \n", + "
\n", + "

session_date

\n", + " calculated attribute\n", + "
\n", + "

avg_weight

\n", + " calculated attribute\n", + "
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-2323.809999465942383
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-2423.299999237060547
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-2523.770000457763672
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-2623.350000381469727
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-2723.389999389648438
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-3022.200000762939453
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-07-3122.139999389648438
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-08-0122.239999771118164
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-08-0222.1200008392334
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-08-0322.360000610351562
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-08-0622.489999771118164
0026c82d-39e4-4c6b-acb3-303eb4b24f052018-08-0722.290000915527344
\n", + "

...

\n", + "

Total: 7898

\n", + " " + ], + "text/plain": [ + "*subject_uuid *session_date avg_weight \n", + "+------------+ +------------+ +------------+\n", + "0026c82d-39e4- 2018-07-23 23.80999946594\n", + "0026c82d-39e4- 2018-07-24 23.29999923706\n", + "0026c82d-39e4- 2018-07-25 23.77000045776\n", + "0026c82d-39e4- 2018-07-26 23.35000038146\n", + "0026c82d-39e4- 2018-07-27 23.38999938964\n", + "0026c82d-39e4- 2018-07-30 22.20000076293\n", + "0026c82d-39e4- 2018-07-31 22.13999938964\n", + "0026c82d-39e4- 2018-08-01 22.23999977111\n", + "0026c82d-39e4- 2018-08-02 22.12000083923\n", + "0026c82d-39e4- 2018-08-03 22.36000061035\n", + "0026c82d-39e4- 2018-08-06 22.48999977111\n", + "0026c82d-39e4- 2018-08-07 22.29000091552\n", + " ...\n", + " (Total: 7898)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# IMPROVED:\n", + "\n", + "weight_date = action.Weighing.proj('weight', session_date='DATE(weighing_time)')\n", + "avg_weight_date = dj.U('subject_uuid', 'session_date').aggr(weight_date, avg_weight='AVG(weight)')\n", + "avg_weight_date" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ORIGINAL\n", + "\n", + "```python\n", + "# NOW DO THE SAME FOR WATER\n", + "water_with_date = action.WaterAdministration.proj('watertype_name', 'water_administered', 'adlib', \n", + " session_date='DATE(administration_time)')\n", + "water_date = (dj.U('subject_uuid', 'session_date') & water_with_date)\n", + "total_water_date = water_date.aggr(water_with_date, total_water='SUM(water_administered)', \n", + " watertype=\"GROUP_CONCAT(DISTINCT watertype_name SEPARATOR '; ')\", \n", + " adlib='MAX(adlib)')\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject_uuid

\n", + " \n", + "
\n", + "

session_date

\n", + " calculated attribute\n", + "
\n", + "

total_water

\n", + " calculated attribute\n", + "
\n", + "

watertype

\n", + " calculated attribute\n", + "
\n", + "

adlib

\n", + " calculated attribute\n", + "
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-02-191.2780000269412994Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-02-210.7230999991297722Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-061.0Water0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-071.0Water0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-080.553600013256073Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-090.16830000281333923Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-110.5508000254631042Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-120.5669999718666077Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-131.617300033569336Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-140.36000001430511475Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-151.9387999773025513Water 10% Sucrose0
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-181.5479999780654907Water 10% Sucrose0
\n", + "

...

\n", + "

Total: 6421

\n", + " " + ], + "text/plain": [ + "*subject_uuid *session_date total_water watertype adlib \n", + "+------------+ +------------+ +------------+ +------------+ +-------+\n", + "00c60db3-74c3- 2019-02-19 1.278000026941 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-02-21 0.723099999129 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-06 1.0 Water 0 \n", + "00c60db3-74c3- 2019-03-07 1.0 Water 0 \n", + "00c60db3-74c3- 2019-03-08 0.553600013256 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-09 0.168300002813 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-11 0.550800025463 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-12 0.566999971866 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-13 1.617300033569 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-14 0.360000014305 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-15 1.938799977302 Water 10% Sucr 0 \n", + "00c60db3-74c3- 2019-03-18 1.547999978065 Water 10% Sucr 0 \n", + " ...\n", + " (Total: 6421)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# IMPROVED\n", + "\n", + "water_with_date = action.WaterAdministration.proj('watertype_name', 'water_administered', 'adlib', \n", + " session_date='DATE(administration_time)')\n", + "total_water_date = dj.U('subject_uuid', 'session_date').aggr(\n", + " water_with_date, \n", + " total_water='SUM(water_administered)', \n", + " watertype=\"GROUP_CONCAT(DISTINCT watertype_name SEPARATOR '; ')\", \n", + " adlib='MAX(adlib)')\n", + "total_water_date" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ORIGINAL\n", + "```python\n", + "session_with_date = behavior.TrialSet.proj('n_trials') \\\n", + " * (acquisition.Session.proj(session_date='DATE(session_start_time)') & 'session_date > \"2019-05-01\"')\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject_uuid

\n", + " \n", + "
\n", + "

session_start_time

\n", + " start time\n", + "
\n", + "

n_trials

\n", + " total trial numbers in this set\n", + "
\n", + "

session_date

\n", + " calculated attribute\n", + "
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-02 20:56:224862019-04-02
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-03 20:46:099962019-04-03
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-04 19:55:124282019-04-04
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-04 20:33:462622019-04-04
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-05 16:00:217862019-04-05
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-08 23:19:007722019-04-08
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-09 19:42:56592019-04-09
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-09 19:48:299282019-04-09
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-04-10 10:37:3610032019-04-10
02120449-9b19-4276-a434-513886c2fb192019-04-02 21:06:189582019-04-02
02120449-9b19-4276-a434-513886c2fb192019-04-03 20:50:119342019-04-03
02120449-9b19-4276-a434-513886c2fb192019-04-04 19:52:126442019-04-04
\n", + "

...

\n", + "

Total: 327

\n", + " " + ], + "text/plain": [ + "*subject_uuid *session_start n_trials session_date \n", + "+------------+ +------------+ +----------+ +------------+\n", + "00c60db3-74c3- 2019-04-02 20: 486 2019-04-02 \n", + "00c60db3-74c3- 2019-04-03 20: 996 2019-04-03 \n", + "00c60db3-74c3- 2019-04-04 19: 428 2019-04-04 \n", + "00c60db3-74c3- 2019-04-04 20: 262 2019-04-04 \n", + "00c60db3-74c3- 2019-04-05 16: 786 2019-04-05 \n", + "00c60db3-74c3- 2019-04-08 23: 772 2019-04-08 \n", + "00c60db3-74c3- 2019-04-09 19: 59 2019-04-09 \n", + "00c60db3-74c3- 2019-04-09 19: 928 2019-04-09 \n", + "00c60db3-74c3- 2019-04-10 10: 1003 2019-04-10 \n", + "02120449-9b19- 2019-04-02 21: 958 2019-04-02 \n", + "02120449-9b19- 2019-04-03 20: 934 2019-04-03 \n", + "02120449-9b19- 2019-04-04 19: 644 2019-04-04 \n", + " ...\n", + " (Total: 327)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# IMPROVED\n", + "\n", + "session_date = behavior.TrialSet.proj(\n", + " 'n_trials', session_date='DATE(session_start_time)') & 'session_date > \"2019-04-01\"'\n", + "session_date" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ORIGINAL\n", + "```python\n", + "# Now you can join (*) the two tables avg_weight_date and session_with_date.\n", + "# Join * will automatically find matched session_date in both tables, and only show entries where\n", + "# these dates exist in both tables. Note there are fewer entries in this resulting table, because\n", + "# on some dates weight is missing and other dates session is missing\n", + "b = total_water_date.aggr(avg_weight_date, keep_all_rows=True)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

subject_uuid

\n", + " \n", + "
\n", + "

session_date

\n", + " calculated attribute\n", + "
\n", + "

n

\n", + " calculated attribute\n", + "
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-02-191
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-02-211
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-061
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-071
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-081
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-091
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-111
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-121
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-131
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-141
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-151
00c60db3-74c3-4ee2-9df9-2c84acf84e922019-03-181
\n", + "

...

\n", + "

Total: 6421

\n", + " " + ], + "text/plain": [ + "*subject_uuid *session_date n \n", + "+------------+ +------------+ +---+\n", + "00c60db3-74c3- 2019-02-19 1 \n", + "00c60db3-74c3- 2019-02-21 1 \n", + "00c60db3-74c3- 2019-03-06 1 \n", + "00c60db3-74c3- 2019-03-07 1 \n", + "00c60db3-74c3- 2019-03-08 1 \n", + "00c60db3-74c3- 2019-03-09 1 \n", + "00c60db3-74c3- 2019-03-11 1 \n", + "00c60db3-74c3- 2019-03-12 1 \n", + "00c60db3-74c3- 2019-03-13 1 \n", + "00c60db3-74c3- 2019-03-14 1 \n", + "00c60db3-74c3- 2019-03-15 1 \n", + "00c60db3-74c3- 2019-03-18 1 \n", + " ...\n", + " (Total: 6421)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# IMPROVED\n", + "b = total_water_date.aggr(avg_weight_date, n=\"count(*)\", keep_all_rows=True)\n", + "b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/QueryCache.ipynb b/notebooks/QueryCache.ipynb new file mode 100644 index 0000000..2c26544 --- /dev/null +++ b/notebooks/QueryCache.ipynb @@ -0,0 +1,228 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Query Caching\n", + "\n", + "Query caching allows avoiding repeated queries to the database by caching the results locally for faster retrieval. \n", + "\n", + "To enable queries, set the query cache local path in `dj.config['query_cache']` and activate the query caching with `conn.set_query_cache(query_cache)` where `conn` is the connection object. \n", + "\n", + "A reference to the connection object is kept as a property of the schema or table objects, e.g. `schema.connection`. Alternatively, the function `dj.conn()` returns the currently active connection object. \n", + "\n", + "The `query_cache` argument is an aribtrary string serving to differentiate cache states; setting a new value will effectively start a new cache, triggering retrieval of new values once.\n", + "\n", + "Setting `query_cache=None` turns off query caching.\n", + "\n", + "While query caching is enabled, any `insert` or `delete` calls and any transactions are disabled and will raise an error. This ensures that stale data are not used for updating the database in violation of data integrity. \n", + "\n", + "To clear the cache, delete the contents of the folder specified in `dj.config['query_cahce']`.\n", + "\n", + "\n", + "## A complete example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "import numpy as np\n", + "import os" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a table to store image data and populate it" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "schema = dj.schema('test_query_caching')\n", + "\n", + "@schema\n", + "class Image(dj.Manual):\n", + " definition = \"\"\"\n", + " image_number : int\n", + " ---\n", + " image : longblob\n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "Image.insert1((1, np.random.randn(300,300)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Time query without caching." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "403 ms ± 72.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "r = (Image & 'image_number=1').fetch1('image')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Enable query caching and note that queries are sped up." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "dj.config['query_cache']=os.path.expanduser('~/tmp')\n", + "conn = dj.conn()\n", + "conn.set_query_cache('s0')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.91 ms ± 57.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit\n", + "r = (Image & 'image_number=1').fetch1('image')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Any attempts to insert or delete data will result in an error while query caching is on." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "ename": "DataJointError", + "evalue": "Only SELECT queries are allowed when query caching is on.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDataJointError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mImage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelete\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36mdelete\u001b[0;34m(self, transaction, safemode)\u001b[0m\n\u001b[1;32m 384\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mtransaction\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 385\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0min_transaction\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 386\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstart_transaction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 387\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 388\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0msafemode\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/connection.py\u001b[0m in \u001b[0;36mstart_transaction\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 333\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0min_transaction\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 334\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Nested connections are not supported.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 335\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'START TRANSACTION WITH CONSISTENT SNAPSHOT'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 336\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_in_transaction\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 337\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Transaction started\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/dev/datajoint-python/datajoint/connection.py\u001b[0m in \u001b[0;36mquery\u001b[0;34m(self, query, args, as_dict, suppress_warnings, reconnect)\u001b[0m\n\u001b[1;32m 273\u001b[0m \u001b[0muse_query_cache\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbool\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_query_cache\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0muse_query_cache\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mre\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mr\"\\s*(SELECT|SHOW)\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mquery\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 275\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Only SELECT queries are allowed when query caching is on.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 276\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0muse_query_cache\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 277\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'query_cache'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDataJointError\u001b[0m: Only SELECT queries are allowed when query caching is on." + ] + } + ], + "source": [ + "Image.delete()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Turn off query caching. Data manua" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Turn off query caching\n", + "conn.set_query_cache(None)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Deleting 1 rows from `test_query_caching`.`image`\n", + "Commit deletes? [yes, No]: yes\n", + "Deletes committed.\n" + ] + } + ], + "source": [ + "Image.delete()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/Question-001.ipynb b/notebooks/Question-001.ipynb new file mode 100644 index 0000000..84095c5 --- /dev/null +++ b/notebooks/Question-001.ipynb @@ -0,0 +1,658 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# User Question 001\n", + "\n", + "A new user asks:\n", + "> Suppose, for example, that there were two experimenters who worked together to collect data for a given session. I understand how to create an Experimenter table and how to make a Session table that includes one experimenter, but I don’t understand how one might indicate that there were multiple experimenters for a given session.\n", + "\n", + "Okay, let's define a minimal example that answers this question.\n", + "\n", + "First, let's create a new database schema:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dimitri@localhost:3306\n" + ] + } + ], + "source": [ + "import datajoint as dj\n", + "schema = dj.schema('test_question001')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's define the `User` set to contain all the lab members who will conduct experiments and we will populate it with a few names." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class User(dj.Manual):\n", + " definition = \"\"\"\n", + " username : varchar(20)\n", + " ---\n", + " full_name='' : varchar(60) \n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# these are just fake names\n", + "User.insert([\n", + " ('ali', 'Ali Cameron'),\n", + " ('david', 'David Petry'),\n", + " ('pat', 'Patricia Avery')\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

username

\n", + " \n", + "
\n", + "

full_name

\n", + " \n", + "
aliAli Cameron
davidDavid Petry
patPatricia Avery
\n", + " \n", + "

Total: 3

\n", + " " + ], + "text/plain": [ + "*username full_name \n", + "+----------+ +------------+\n", + "ali Ali Cameron \n", + "david David Petry \n", + "pat Patricia Avery\n", + " (Total: 3)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "User()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's define the `Session` set. Since there may be mulitple experimenters, we will not put the experimenter in the session. Rather, we will create a separate `Experimenter` set. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Session(dj.Manual):\n", + " definition = \"\"\"\n", + " # Experiment session\n", + " session : int # session number\n", + " ---\n", + " session_date : date \n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Experimenter(dj.Manual):\n", + " definition = \"\"\"\n", + " # Persons performing experiments in the session\n", + " -> Session\n", + " -> User\n", + " \"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that both `Session` and `User` are in the primary key of `Experimenter`. This means that multiple users can be experimenters in each session. \n", + "\n", + "Let's add some sessions and experimenters that conducted them:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "Session.insert((\n", + " [1, '2018-04-09'],\n", + " [2, '2019-02-07'],\n", + " [3, '2019-03-31']\n", + "))\n", + "\n", + "Experimenter.insert([\n", + " (1, 'pat'),\n", + " (1, 'ali'),\n", + " (2, 'pat'),\n", + " (3, 'pat'),\n", + " (3, 'david') \n", + "])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We are done! Here is our new schema:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "User\n", + "\n", + "\n", + "User\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Experimenter\n", + "\n", + "\n", + "Experimenter\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "User->Experimenter\n", + "\n", + "\n", + "\n", + "\n", + "Session\n", + "\n", + "\n", + "Session\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Session->Experimenter\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's try some queries: " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Experiment session\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

session

\n", + " session number\n", + "
\n", + "

session_date

\n", + " \n", + "
12018-04-09
22019-02-07
32019-03-31
\n", + " \n", + "

Total: 3

\n", + " " + ], + "text/plain": [ + "*session session_date \n", + "+---------+ +------------+\n", + "1 2018-04-09 \n", + "2 2019-02-07 \n", + "3 2019-03-31 \n", + " (Total: 3)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# All sessions\n", + "Session()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

session

\n", + " session number\n", + "
\n", + "

username

\n", + " \n", + "
\n", + "

session_date

\n", + " \n", + "
\n", + "

full_name

\n", + " \n", + "
1ali2018-04-09Ali Cameron
3david2019-03-31David Petry
1pat2018-04-09Patricia Avery
2pat2019-02-07Patricia Avery
3pat2019-03-31Patricia Avery
\n", + " \n", + "

Total: 5

\n", + " " + ], + "text/plain": [ + "*session *username session_date full_name \n", + "+---------+ +----------+ +------------+ +------------+\n", + "1 ali 2018-04-09 Ali Cameron \n", + "3 david 2019-03-31 David Petry \n", + "1 pat 2018-04-09 Patricia Avery\n", + "2 pat 2019-02-07 Patricia Avery\n", + "3 pat 2019-03-31 Patricia Avery\n", + " (Total: 5)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# All Session x Experimenter combinations\n", + "Session * Experimenter * User" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Experiment session\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "
\n", + "

session

\n", + " session number\n", + "
\n", + "

session_date

\n", + " \n", + "
12018-04-09
\n", + " \n", + "

Total: 1

\n", + " " + ], + "text/plain": [ + "*session session_date \n", + "+---------+ +------------+\n", + "1 2018-04-09 \n", + " (Total: 1)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# All Sessions conducted by Ali\n", + "Session & (Experimenter & {'username': 'ali'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "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.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Question-002.ipynb b/notebooks/Question-002.ipynb new file mode 100644 index 0000000..14c68a6 --- /dev/null +++ b/notebooks/Question-002.ipynb @@ -0,0 +1,615 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# User Question 002\n", + "\n", + "A new user asks:\n", + "> We have the notion of a list of electrodes, which would fit nicely into an Electrode table, but then data are taken from groups of electrodes at a time (e.g. a tetrode, a 32 channel shank of a polymer probe, etc). Neurodata Without Borders (nwb.org) handles that with a table that indicates, for every data object, which electrodes were included. How are these sorts of references possible in DataJoint? \n", + "\n", + "Similar designs have been deployed by many DataJoint-based ephys schemas. \n", + "For example, here is on from the International Brain Lab https://github.com/int-brain-lab/IBL-pipeline/blob/master/ibl_pipeline/ephys.py\n", + "and another from the Mesoscale Activity Project https://github.com/mesoscale-activity-map/map-ephys/blob/master/pipeline/ephys.py\n", + "These schemas were designed to closely match NWB conventions and names.\n", + "\n", + "Let's define a minimal example:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's work with an existing schema defining experiment sessions and we will add ephys probes in our new schema within this notebook. \n", + "\n", + "Imagine that the schema designed in [User Question 001](Question-001.ipynb) can is defined in a module called `experiment.py`. Then we would be able to import it as \n", + "\n", + "```python\n", + "import experiment\n", + "```\n", + "\n", + "and access all its tables.\n", + "\n", + "However, that schema was defined in a notebook. DataJoint provides function `dj.create_virtual_module` to mimic importing a virtual module by reconstructing it from the tables in the database." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "experiment = dj.create_virtual_module('experiment', 'test_question001')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can view the schema from [User Question 1](Question-001.ipynb), and access any of its data:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's define the `User` set to contain all the lab members who will conduct experiments and we will populate it with a few names." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "experiment.User\n", + "\n", + "\n", + "experiment.User\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Experimenter\n", + "\n", + "\n", + "experiment.Experimenter\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "experiment.User->experiment.Experimenter\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Session\n", + "\n", + "\n", + "experiment.Session\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Session->experiment.Experimenter\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(experiment)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Experiment session\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "
\n", + "

session

\n", + " session number\n", + "
\n", + "

session_date

\n", + " \n", + "
12018-04-09
22019-02-07
32019-03-31
\n", + " \n", + "

Total: 3

\n", + " " + ], + "text/plain": [ + "*session session_date \n", + "+---------+ +------------+\n", + "1 2018-04-09 \n", + "2 2019-02-07 \n", + "3 2019-03-31 \n", + " (Total: 3)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "experiment.Session()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's define a new schema for working with electrophysiology probes for the experiment session." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "schema = dj.schema('test_question002')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's define the `Probe` set to define various available probes. We choose to define as a lookup table, meaning that its contents is fairly static and is populated from the `contents` property of its class." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class Probe(dj.Lookup):\n", + " definition = \"\"\"\n", + " # Ephys probe\n", + " probe_part_no : varchar(20)\n", + " ---\n", + " probe_type : varchar(32)\n", + " probe_comment : varchar(4000)\n", + " \"\"\"\n", + " contents = [\n", + " ('15131808323', 'neuropixels probe O3', ''),\n", + " ('H-194', 'janelia2x32', '')\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's define the `ElectrodeGroup` for a given session. These are electrodes on a probe. Multiple groups can be used in a single session. Hence, we add the `electrode_group` attribute to the primary key. We add `Probe` as a secondary attribute. We then add the `Electrode` set as a [part table](https://docs.datajoint.io/python/computation/03-master-part.html) of `ElectrodeGroup`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "@schema\n", + "class ElectrodeGroup(dj.Manual):\n", + " definition = \"\"\"\n", + " # Electrode\n", + " -> experiment.Session\n", + " electrode_group : tinyint # Electrode_group is like the probe\n", + " ---\n", + " -> Probe\n", + " \"\"\"\n", + "\n", + " class Electrode(dj.Part):\n", + " definition = \"\"\"\n", + " -> master\n", + " electrode : smallint # sites on the electrode\n", + " \"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is the entire pipeline with both schemas:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "%3\n", + "\n", + "\n", + "\n", + "Probe\n", + "\n", + "\n", + "Probe\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ElectrodeGroup\n", + "\n", + "\n", + "ElectrodeGroup\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Probe->ElectrodeGroup\n", + "\n", + "\n", + "\n", + "\n", + "ElectrodeGroup.Electrode\n", + "\n", + "\n", + "ElectrodeGroup.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "ElectrodeGroup->ElectrodeGroup.Electrode\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Experimenter\n", + "\n", + "\n", + "experiment.Experimenter\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Session\n", + "\n", + "\n", + "experiment.Session\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Session->ElectrodeGroup\n", + "\n", + "\n", + "\n", + "\n", + "experiment.Session->experiment.Experimenter\n", + "\n", + "\n", + "\n", + "\n", + "experiment.User\n", + "\n", + "\n", + "experiment.User\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "experiment.User->experiment.Experimenter\n", + "\n", + "\n", + "\n", + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dj.Diagram(schema) + dj.Diagram(experiment)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's populate an electrode group for Session 1 with 32 electrodes" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "key = dict(session=2, electrode_group=1)\n", + "ElectrodeGroup.insert1(dict(key, probe_part_no='H-194'))\n", + "ElectrodeGroup.Electrode.insert(dict(key, electrode=i) for i in range(32))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is the result:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

session

\n", + " session number\n", + "
\n", + "

electrode_group

\n", + " Electrode_group is like the probe\n", + "
\n", + "

electrode

\n", + " sites on the electrode\n", + "
\n", + "

probe_part_no

\n", + " \n", + "
210H-194
211H-194
212H-194
213H-194
214H-194
215H-194
216H-194
\n", + "

...

\n", + "

Total: 32

\n", + " " + ], + "text/plain": [ + "*session *electrode_gro *electrode probe_part_no \n", + "+---------+ +------------+ +-----------+ +------------+\n", + "2 1 0 H-194 \n", + "2 1 1 H-194 \n", + "2 1 2 H-194 \n", + "2 1 3 H-194 \n", + "2 1 4 H-194 \n", + "2 1 5 H-194 \n", + "2 1 6 H-194 \n", + " ...\n", + " (Total: 32)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ElectrodeGroup * ElectrodeGroup.Electrode & key" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Have fun with DataJoint!" + ] + } + ], + "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.6.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/UUID.ipynb b/notebooks/UUID.ipynb index 75a6918..e7a3baf 100644 --- a/notebooks/UUID.ipynb +++ b/notebooks/UUID.ipynb @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -24,18 +24,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function uuid1 in module uuid:\n", + "\n", + "uuid1(node=None, clock_seq=None)\n", + " Generate a UUID from a host ID, sequence number, and the current time.\n", + " If 'node' is not given, getnode() is used to obtain the hardware\n", + " address. If 'clock_seq' is given, it is used as the sequence number;\n", + " otherwise a random 14-bit sequence number is chosen.\n", + "\n" + ] + } + ], "source": [ "help(uuid.uuid1)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[UUID('8d552590-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552591-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552592-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552593-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552594-b940-11e9-9213-7470fdf23ef1')]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# use the current hardware address and time\n", "[uuid.uuid1() for _ in range(5)]" @@ -43,9 +73,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[UUID('8d552595-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552596-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552597-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552598-b940-11e9-9213-7470fdf23ef1'),\n", + " UUID('8d552599-b940-11e9-9213-7470fdf23ef1')]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# use the current hardware address and time\n", "[uuid.uuid1() for _ in range(5)]" @@ -53,9 +98,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[UUID('8e345e3a-b940-11e9-8001-7470fdf23ef1'),\n", + " UUID('8e346188-b940-11e9-8001-7470fdf23ef1'),\n", + " UUID('8e34629c-b940-11e9-8001-7470fdf23ef1'),\n", + " UUID('8e346370-b940-11e9-8001-7470fdf23ef1'),\n", + " UUID('8e346438-b940-11e9-8001-7470fdf23ef1')]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# use fixed values\n", "[uuid.uuid1(None, 1) for _ in range(5)]" @@ -63,27 +123,66 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function uuid3 in module uuid:\n", + "\n", + "uuid3(namespace, name)\n", + " Generate a UUID from the MD5 hash of a namespace UUID and a name.\n", + "\n" + ] + } + ], "source": [ "help(uuid.uuid3)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function uuid5 in module uuid:\n", + "\n", + "uuid5(namespace, name)\n", + " Generate a UUID from the SHA-1 hash of a namespace UUID and a name.\n", + "\n" + ] + } + ], "source": [ "help(uuid.uuid5)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(UUID('345b4a08-7955-5b86-8646-f0826799afe9'),\n", + " UUID('b5804c3f-57b1-54e3-8176-3b45aa443a97'),\n", + " UUID('58571fff-c6bd-583f-88ac-ef0b8ff2981f'),\n", + " UUID('b5804c3f-57b1-54e3-8176-3b45aa443a97'),\n", + " UUID('6340129b-3a59-5354-aec6-5df769ae2ce7'))" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "top = uuid.UUID('00000000-0000-0000-0000-000000000000')\n", "topic = uuid.uuid5(top, 'Neuroscience')\n", @@ -99,36 +198,93 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "UUID('3d9d9035-dec3-5fc8-b66c-38cd8537acbe')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "uuid.uuid5(subject4, 'study'*1000000)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function uuid4 in module uuid:\n", + "\n", + "uuid4()\n", + " Generate a random UUID.\n", + "\n" + ] + } + ], "source": [ "help(uuid.uuid4)" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[UUID('7b946ea0-17f1-48b6-83d3-f78a9b6a7c94'),\n", + " UUID('baa17a66-c87e-4961-9ffc-a3ac43d113dd'),\n", + " UUID('8d12a6dc-8a63-4f89-9d3a-6d7a1b8b2611'),\n", + " UUID('27c51ddf-e360-491d-8dd5-15238967b2b3'),\n", + " UUID('79cd953d-8e26-45ca-85a3-ae458387a65e'),\n", + " UUID('8500f53d-2026-4891-913a-62ffced44c81'),\n", + " UUID('d2648a4c-25e8-4094-a3bc-c9b22d35b466'),\n", + " UUID('dee8b4c3-a9e0-4820-bffc-a8c8504c6203'),\n", + " UUID('6cda1007-b98f-42f5-83ce-e96b9a6d5b42'),\n", + " UUID('56b73a84-7097-4af1-82d7-02665833d222'),\n", + " UUID('7d3c579c-76c9-4f7e-97bf-71b6001eb164'),\n", + " UUID('b93f2f0d-938b-4ff6-bca0-6998557e742d')]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "[uuid.uuid4() for _ in range(12)]" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(UUID('5b1e88c5-b46d-4f47-afaf-08b6d5d585eb'),\n", + " '5b1e88c5-b46d-4f47-afaf-08b6d5d585eb')" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a = uuid.uuid4()\n", "s = str(a)\n", @@ -137,27 +293,61 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "UUID('5b1e88c5-b46d-4f47-afaf-08b6d5d585eb')" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(UUID('5b1e88c5-b46d-4f47-afaf-08b6d5d585eb'),\n", + " '5b1e88c5-b46d-4f47-afaf-08b6d5d585eb')" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a, s" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "UUID('5b1e88c5-b46d-4f47-afaf-08b6d5d585eb')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "uuid.UUID(s)" ] @@ -171,9 +361,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'0.12.dev5'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import datajoint as dj\n", "dj.__version__" @@ -181,16 +382,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connecting dimitri@localhost:3306\n" + ] + } + ], "source": [ "schema = dj.schema('dimitri_uuid')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -203,6 +412,26 @@ " \"\"\"" ] }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "message_id : uuid # internal message id\n", + "---\n", + "message_body : varchar(1000) \n", + "\n" + ] + } + ], + "source": [ + "Message.describe();" + ] + }, { "cell_type": "code", "execution_count": null, @@ -386,7 +615,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/notebooks/Update1.ipynb b/notebooks/Update1.ipynb new file mode 100644 index 0000000..2883d3e --- /dev/null +++ b/notebooks/Update1.ipynb @@ -0,0 +1,348 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# `update1`\n", + "## Updating values in a table" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In DataJoint, the principal way of replacing data is by `delete` and `insert`. This approach observes referential integrity constraints. \n", + "\n", + "In some cases, it becomes necessary to deliberately correct existing values. The `update1` method accomplishes this. The method should only be used to fix problems, and not as part of a regular workflow. When updating an entry, make sure that any information stored in dependent tables that depends on the update values is properly updated as well. \n", + "\n", + "Syntax:\n", + "\n", + "```python\n", + "table.update1(record)\n", + "```\n", + "Here `record` is a `dict` specifying the primary key values for identifying what record to update and the values that should be updated. The entry must already exist.\n", + "\n", + "## Example\n", + "Let's create the `Student` table and populate a few entries." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import datajoint as dj\n", + "schema = dj.schema('test_update')\n", + "\n", + "@schema\n", + "class Student(dj.Manual):\n", + " definition = \"\"\"\n", + " student_id : int\n", + " ---\n", + " full_name : varchar(100) # last_name, first_name middle_name\n", + " phone=\"\": varchar(20)\n", + " sex : enum('female', 'male')\n", + " \"\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "Student.insert1(dict(student_id=303, full_name=\"Rosen, Rose\", sex=\"female\"))\n", + "Student.insert1(dict(student_id=304, full_name=\"Rosen, Rose\", sex=\"male\", phone=\"(813)555-3744\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

student_id

\n", + " \n", + "
\n", + "

full_name

\n", + " last_name, first_name middle_name\n", + "
\n", + "

phone

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
303Rosen, Rosefemale
304Rosen, Rose(813)555-3744male
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*student_id full_name phone sex \n", + "+------------+ +------------+ +------------+ +--------+\n", + "303 Rosen, Rose female \n", + "304 Rosen, Rose (813)555-3744 male \n", + " (Total: 2)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Student()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now update some values. Note that you must specify the primary key and the entry must already exist." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

student_id

\n", + " \n", + "
\n", + "

full_name

\n", + " last_name, first_name middle_name\n", + "
\n", + "

phone

\n", + " \n", + "
\n", + "

sex

\n", + " \n", + "
303Rosen, Rose(813)555-7133female
304Ramesh, Henry(813)555-3744male
\n", + " \n", + "

Total: 2

\n", + " " + ], + "text/plain": [ + "*student_id full_name phone sex \n", + "+------------+ +------------+ +------------+ +--------+\n", + "303 Rosen, Rose (813)555-7133 female \n", + "304 Ramesh, Henry (813)555-3744 male \n", + " (Total: 2)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Student.update1(dict(student_id=303, phone=\"(813)555-7133\"))\n", + "Student.update1(dict(student_id=304, full_name=\"Ramesh, Henry\"))\n", + "Student()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the entry does not exist or if the primary key value is not specified, `update1` raises errors:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "ename": "DataJointError", + "evalue": "Update entry must exist.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDataJointError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mStudent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstudent_id\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m305\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mphone\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"(800)555-3377\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36mupdate1\u001b[0;34m(self, row)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mrow\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mk\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprimary_key\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m \u001b[0;34m&\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Update entry must exist.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0;31m# UPDATE query\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0mrow\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__make_placeholder\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrow\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mk\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprimary_key\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDataJointError\u001b[0m: Update entry must exist." + ] + } + ], + "source": [ + "Student.update1(dict(student_id=305, phone=\"(800)555-3377\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "ename": "DataJointError", + "evalue": "The argument of update1 must supply all primary key values.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDataJointError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mStudent\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mphone\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"(800)555-3377\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/dev/datajoint-python/datajoint/table.py\u001b[0m in \u001b[0;36mupdate1\u001b[0;34m(self, row)\u001b[0m\n\u001b[1;32m 228\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'The argument of update1 must be dict-like.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 229\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0missuperset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprimary_key\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 230\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'The argument of update1 must supply all primary key values.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 231\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 232\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mDataJointError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Attribute `%s` not found.'\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mk\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mk\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrow\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mk\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mheading\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDataJointError\u001b[0m: The argument of update1 must supply all primary key values." + ] + } + ], + "source": [ + "Student.update1(dict(phone=\"(800)555-3377\"))" + ] + } + ], + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}