diff --git a/.travis.yml b/.travis.yml
index 1f4aedb..fd6bba6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,19 @@
language: python
python:
- '2.7'
-- '3.3'
+#- '3.3' # Bug in TravisCI prevents 3.3 tests running (old pip in path)
- '3.4'
- '3.5'
- '3.6'
install:
- pip install -e .
-- pip install pytest pytest-cov coveralls
+- pip install pytest pytest-cov coveralls flake8
env:
- TEST_ENV=travis
script:
-- travis_wait 50 py.test
+- flake8 . && travis_wait 50 py.test
before_install:
-- openssl aes-256-cbc -K $encrypted_4b438100ad6f_key -iv $encrypted_4b438100ad6f_iv
+- openssl aes-256-cbc -K $encrypted_39a50b90a369_key -iv $encrypted_39a50b90a369_iv
-in travis.tar.enc -out travis.tar -d
- tar xvf travis.tar
- mkdir -p ~/mdf/credentials/
diff --git a/README.md b/README.md
index b4b315c..17c8cae 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ r = mdf.search("materials commons")
r_2 = mdf.search_by_elements(elements=["Al","Cu"], sources=["oqmd"])
```
-More examples are available in the examples directory.
+More examples are available in the docs/examples directory.
# Documentation
diff --git a/examples/Example Aggregations.ipynb b/docs/examples/Example Aggregations.ipynb
similarity index 100%
rename from examples/Example Aggregations.ipynb
rename to docs/examples/Example Aggregations.ipynb
diff --git a/docs/examples/Example Analysis - Fe-Cr-Al Oxidation.ipynb b/docs/examples/Example Analysis - Fe-Cr-Al Oxidation.ipynb
new file mode 100644
index 0000000..755f9b8
--- /dev/null
+++ b/docs/examples/Example Analysis - Fe-Cr-Al Oxidation.ipynb
@@ -0,0 +1,291 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Example Analysis\n",
+ "\n",
+ "### A high-throughput investigation of Fe–Cr–Al as a novel high-temperature coating for nuclear cladding materials\n",
+ "\n",
+ "Article Link\n",
+ "\n",
+ "\"Bunn, Jonathan Kenneth, Randy L. Fang, Mark R. Albing, Apurva Mehta, Matthew J. Kramer, Matthew F. Besser, and Jason R. Hattrick-Simpers. \"A high-throughput investigation of Fe–Cr–Al as a novel high-temperature coating for nuclear cladding materials.\" Nanotechnology 26, no. 27 (2015): 274003.\"\n",
+ "\n",
+ "\n",
+ "Example: We want to plot some of the data from the above study using MDF.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from io import StringIO\n",
+ "from multiprocessing.pool import Pool\n",
+ "\n",
+ "from mdf_forge.forge import Forge\n",
+ "\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import seaborn as sns\n",
+ "\n",
+ "%matplotlib inline\n",
+ "sns.set_context('poster')\n",
+ "sns.set_style('white')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Retrieve Records"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Found 1401 matches\n"
+ ]
+ }
+ ],
+ "source": [
+ "mdf = Forge()\n",
+ "res = mdf.match_field(\"mdf.source_name\",\"fe_cr_al_oxidation\").match_field(\"mdf.resource_type\", \"record\").search()\n",
+ "print(\"Found {results} matches\".format(results=len(res)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'fe_cr_al_oxidation': {'atomic_composition_percent': {'Al': 3.1,\n",
+ " 'Cr': 26.5,\n",
+ " 'Fe': 70.5},\n",
+ " 'temperature_k': 420.0},\n",
+ " 'mdf': {'collection': 'Fe-Cr-Al Oxidation Studies',\n",
+ " 'composition': 'FeCrAl',\n",
+ " 'elements': ['Cr', 'Fe', 'Al'],\n",
+ " 'ingest_date': '2017-08-04T21:26:53.296946Z',\n",
+ " 'links': {'csv': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
+ " 'http_host': 'https://data.materialsdatafacility.org',\n",
+ " 'path': '/collections/Fe_Cr_Al_data/420 K/420 K Point 104.txt'},\n",
+ " 'landing_page': 'https://materialsdata.nist.gov/dspace/xmlui/handle/11256/836#104',\n",
+ " 'parent_id': '5984e69cf2c00439c790bf54'},\n",
+ " 'mdf_id': '5984e69df2c00439c790bfbc',\n",
+ " 'metadata_version': '0.3.2',\n",
+ " 'resource_type': 'record',\n",
+ " 'scroll_id': 104,\n",
+ " 'source_name': 'fe_cr_al_oxidation',\n",
+ " 'tags': ['csv'],\n",
+ " 'title': 'Fe-Cr-Al Oxidation - 420 K Point 104'}}"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "res[0]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Download data using HTTP"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def format_get_cr_al_params(d):\n",
+ " tmp_d = {}\n",
+ " for key in d['atomic_composition_percent']:\n",
+ " tmp_d[key] = float(d['atomic_composition_percent'][key])\n",
+ " tmp_d['temperature_k'] = float(d['temperature_k']) if d['temperature_k'] != \"Room\" else 297.15 # Avg room temp\n",
+ " return tmp_d\n",
+ " \n",
+ "def get_fe_cr_al(r):\n",
+ " res = next(mdf.http_stream(r))\n",
+ " \n",
+ " params = format_get_cr_al_params(r['fe_cr_al_oxidation']) \n",
+ " \n",
+ " df = pd.read_csv(StringIO(res), sep=\"\\t\", header=None, names=[\"twotheta\",\"counts\"])\n",
+ " return (params, df)\n",
+ "\n",
+ "n_workers = 10\n",
+ "n_points = 300\n",
+ "\n",
+ "mp = Pool(n_workers)\n",
+ "mdf_data = mp.map(get_fe_cr_al, res[:n_points])\n",
+ "mp.close()\n",
+ "mp.join()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Aggregate Results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "## Choose peak integration range (this is ~ a range for the Fe3O4 oxide)\n",
+ "\n",
+ "integration_peak = 42.8\n",
+ "integration_width = 1.\n",
+ "integration_range = (integration_peak-integration_width,integration_peak+integration_width)\n",
+ "\n",
+ "results = []\n",
+ "\n",
+ "for data, df_tmp in mdf_data:\n",
+ " result = {\"data\":{},\"aggregation\":0}\n",
+ " result['data'] = data\n",
+ " agg = df_tmp[(df_tmp.twotheta>integration_range[0]) & \n",
+ " (df_tmp.twotheta"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "x = [r[\"data\"][\"Al\"] for r in results]\n",
+ "y = [r[\"data\"][\"Cr\"] for r in results]\n",
+ "s = [[r[\"aggregation_norm\"]*1000 for r in results]]\n",
+ "\n",
+ "fig, ax = plt.subplots()\n",
+ "plt.scatter(x, y, s=s, alpha=0.6)\n",
+ "ax.set_xlabel(\"Al Atomic %\")\n",
+ "ax.set_ylabel(\"Cr Atomic %\")\n",
+ "sns.despine()\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Joint Plots (Where we have observations)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n/vx5bN68GWq1Wu5IQqusrMSdO3ewePFi+Pv7a7+A+5fY+fv7o7i4WOaUYnnc/29emqjf+LcrKI1G\ngzNnzqCyslI7VlVVhVOnTkGj0ciYTFxtbW1ISEjAsWPHsHHjRnh5eckdSXhubm7IyMjo9AUAkydP\nRkZGBlxcXGROKZbg4GAAQFFRUYfxw4cPQ6FQwN7eXo5Y1Ed4TltQ06dPx65duxATE4P4+HgYGRlh\n3bp1UCgUmDFjhtzxhPT5558jPz8f0dHRsLCwwK+//qrdplAoeJj8KVhbW2P06NFdbnN0dHzoNnq4\nV199FaNHj0ZiYiJu3rypfSNaUVER339hAIwkSZLkDkFPp6amBklJSThy5AgkSUJAQACWLl0KJycn\nuaMJSaPRoLq6usttsbGxWLBgQR8n0l9qtRrR0dFYuHCh3FGE1NjYiNTUVOzfvx8NDQ0YOnQo5s6d\ni7CwMLmjUS9jaRMREQmC57SJiIgEwdImIiISBEubiIhIECxtIiIiQbC0iYiIBMHSJiIiEgRLm+gB\nmZmZUKvVOH78uNxRepVGo8Hbb78tdwwi6gaWNhmU8vJyqNVqqNVqnDt3rkfnTkhIgFqtxrx58x66\nz/bt25GZmdmjj6srLl26hIiICHh7eyM0NBTZ2dmd9mltbcXUqVOxdu1aGRISiY+lTQYlKysLlpaW\nsLOzQ1ZWVo/N29jYiAMHDsDZ2RlFRUW4du1al/tlZGT06OM+i/z8fGzZsqVH5mptbUVsbCzq6uqw\nePFiuLu7Y8mSJR0+Cha4//ybmpoQExPTI49LZGhY2mQw2trakJ2djdDQUEyaNAl5eXloaWnpkbnz\n8vJw584dpKamAgBycnJ6ZN7eZG5uDnNz8x6Zq7y8HGVlZVi+fDlmzZqF1atXY9CgQSgoKNDuc+XK\nFaxfvx6JiYno169fjzwukaFhaZPBOHr0KGprazFlyhRMnToVt27dQmFhYY/MnZWVBV9fX3h6euLf\n//53l6tptVqN6upqnDhxQnuI/sFbf3799deYPHkyPDw84O/vj+joaFy8eLHDPlVVVVCr1UhLS8P3\n33+PsLAweHh44LXXXsPBgwcBABcvXkRUVBS8vb0REBCAdevW4cFPLH7YOe2ffvoJkZGR8PPzg6en\nJyZMmPDYG1HcuXMHADBgwAAAgJGREaytrdHc3KzdZ8WKFQgMDMS4ceMeORcRPRxLmwxGZmYmHBwc\n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\nRU5ErkJggg==\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "ax = sns.jointplot(np.asarray(x), np.asarray(y), kind=\"kde\", \n",
+ " shade=True, stat_func=None, size=7).set_axis_labels(\"Al Atomic %\", \"Cr Atomic %\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\nh/r6eug6P6MhIiIqlCGF9s6dO3HppZdi48aN3ZdNmTIF99xzD6ZPn5634YiIiGifIdXjN998M6ZM\nmYKXXnoJH330EZ566ikEg0HcdNNN+Z6PiIiIsvoN7SeffLLXy9evX49ly5Zh8uTJCAQCmDNnDs49\n91xs2LChIEMSERHRAKF911134fzzz8emTZsOuHzatGl44oknkMlkAABtbW14/vnnMWXKlMJNSkRE\nNML1G9rPP/88jjjiCPzDP/wD7rzzzu6Q/tGPfoQXX3wRxx13HBYuXIiFCxfi008/xfXXX1+UoYmI\niEaifg9EKysrww033IC/+7u/w0033YTnnnsON910ExYuXIiXXnoJa9asQXNzM8aNG4eTTz4ZlZWV\nxZqbiIhoxHF09PhRRx2FJ598Eo888gi+973v4Utf+hKuv/56nHHGGYWej4iIiLIcHz2uaRq+/e1v\n4/nnn4dlWTjttNPw6KOPQilvnUGJiIjIqwYM7cbGRjzxxBN4+OGH8dFHH2HChAm46667cNttt2HF\nihU466yz8MknnxRjViIiohGt33r81VdfxRVXXAEACAaDiMVi+O53v4urrroKJ598MhYsWIC7774b\n5557Ls4991xceeWViEQiRRmciIhopOl3pX3bbbdh/vz5WLt2LdauXYurr74a9913H/bu3QsACIVC\nuOaaa/A///M/+Oijj3DaaacVZWgiIqKRqN/Q3rFjBxYtWoRgMAgA+NrXvgYpJXbu3HnA7WbNmoXH\nHnsMl19+eeEmJSIiGuH6De2ZM2di1apVaG5uRjwexyOPPAK/34+pU6f2evuzzjqrEDMSERERBvhM\n+4YbbsBll12Gk08+GQCg6zquu+46jBo1qhizERER0X76De25c+fif//3f/H+++8jnU5jzpw5qKmp\nKdZsREREtJ8BT65SVlaGhQsXFmMWIiIi6seQfjUnERERFR9Dm4iIyCMY2kRERB7B0CYiIvIIhjYR\nEZFHOPrVnIXy6quv4r777sOmTZvQ0dGBqqoqzJs3D1dccQVmzpzZfbtdu3bhlltuwV/+8hcopXDi\niSfiRz/6EWpra12cnoiIqLgcr7Tvu+8+nHPOOX1ef+655+KBBx4Y1IN3dHRgzpw5uPHGG7FixQr8\nv//3/7Bx40acffbZ3adKTSaT+Pa3v43NmzfjP/7jP3Drrbdi27ZtuOCCC5BIJAb1eERERF7meKX9\n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\nqA+OP9NesGABfvKTn+Cb3/wm5s2bh9raWmjagZk/2NOYEhERkXOOQ/vdd9/FtddeC9M08fbbb/d6\nG4Y2ERFR4TgO7X//939HKBTCf/7nf+KYY45BeXl5IeciIiKigzgO7U2bNuHKK6/El7/85ULOQ0RE\nRH1wfCBadfXgf7sKERER5Y/j0P7Wt76Fp556CqlUqpDzEBERUR8c1+NlZWUIhUL42te+hm984xuo\nra2Frus9bvf3f//3eR2QiIiIbI5D+9prr+3+37/85S97vY0QgqFNRERUII5D++GHHy7kHERERDQA\nx6F9/PHHF3IOIiIiGoDjA9EO1traitbW1nzOQkRERP1wvNIGgKamJtxxxx145ZVXEI/HAQDRaBSL\nFi3CVVddxd+xTUREVECOQ7uhoQHnnHMOWlpaUF9fj5kzZwIANm7ciGeeeQavv/46fve732HSpEkF\nG5aIiGgkcxzad955J+LxOB544AGccMIJB1y3du1aLF26FP/1X/+F22+/Pe9DEhER0SA+037jjTdw\n/vnn9whsAJg/fz7OO+88/OUvf8nrcERERLSP49Du7OzExIkT+7y+trYWXV1deRmKiIiIenIc2pMm\nTcJrr73W5/WvvfZav6FOREREw+M4tM844wysWbMG119/PbZv3959+fbt23HjjTfiT3/6E77xjW8U\nZEgiIiIaxIFo//zP/4xPPvkETz31FJ5++mn4/X4AgGEYUErh1FNPxSWXXFKwQYmIiEY6x6Ht8/lw\n11134dVXX8Xq1avR0NAAAJg8eTIWL16Mk046qWBDEhER0SBPrgIACxcuxMKFCwsxCxEREfXD8Wfa\nixcvxurVq/u8/pVXXsHixYvzMhQRERH15Di0d+7ciUQi0ef1yWQSjY2NeRlqpFFKobHTwMe709gR\nM2BK5fZIjuTm3taeQWvShFTemFsqhZaEib0JE0nDgvLI3AJAQBdujzFoQV1gfFRHxK9B89D4llQI\n6AIhXcArYyul0Jo0sbU9g70J77wnyblB1+N92bVrFyKRSL7ubsToykh8sCuJWFrCUkBHSiKWzqA6\nqqMqrEOI0txcxNIWNrYaMKSCzM7dmZYYF/Uh4h/y76EpKKUU4obE3oQFpQAFoCujkDQtlAd0+Es4\nEP2aHdil+nrojSaA6jIdo8N6d+iFfQKGBDJW6YaJVAoZ035dCyHg0wFds2c2pNvT9S1pSDTETGSy\n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\ntxTtSDscDlNaWioj8wbdrmkLIYQQVmWRecpCCCGEkKYthBBCWIQ0bSGEEMIipGkLIYQQFiFNWwgh\nhLAIadpCCCGERUjTFkIIISxCmrYQQghhEdK0hRBCCIuQa8LFQGlpKU899RSFhYUUFRXh9/vlurvt\nWLt2LWvWrKGwsJCDBw+SmZnJhRdeyM0330xiYqLZ5cWdjz76iKeeeort27dTWVmJz+dj9OjRzJo1\niyFDhphdniXccMMNbNy4kZkzZ/LrX//a7HLizqZNm7juuuuOWZ6UlMRnn31mQkXdlzTtGCguLuad\nd94hNzeXMWPGsHHjRrNLimvLly8nMzOTX//612RkZPD111/z+OOPs2nTJl599VVUVQKi5iorK8nN\nzeWaa67B5/NRUlLCU089xU9+8hP++te/0qdPH7NLjGurV69m69atZpdhCf/93//NyJEjm36X72+I\nPWnaMTB27Fg+/vhjAF577TVp2sexdOlSfD5f0+8FBQWkpqZy5513smnTJs4880wTq4s/l156KZde\nemmLZXl5eVx88cW8++67XH/99SZVFv8qKytZtGgRCxYsYO7cuWaXE/cGDx7MqFGjzC6jW5MhSwzI\nyLBjmjfsRo1H9/v37491OZaUmpoKyEjoeJYsWcIpp5xyzEGPEPFKRtrCEj799FMgcqQvWqdpGpqm\nUVJSwgMPPEBaWpo0o3Z89tlnrFq1ijfftOq32sfevHnzOHz4MMnJyYwfP565c+eSlZVldlndijRt\nEff279/Po48+yllnndXifJpo6aqrruI///kPAP379+f555+nZ8+eJlcVn4LBIHfffTfXX389gwYN\nMrucuJeUlMT111/P2LFjSUxM5Ouvv2bZsmV8+umnrFq1Sl5nMSRNW8S12tpabrnlFmw2G4sWLTK7\nnLi2ePFiampq2L17N8uXL2fGjBm8/PLL8imFVjz99NP4/X5uueUWs0uxhBEjRjBixIim3wsKChg7\ndixXXXUVK1askBn3MSQnW0Xc8vv9zJw5kz179vDMM8+QkZFhdklxbfDgweTn53PppZfy3HPPUVdX\nx5NPPml2WXGnpKSEpUuXMmfOHILBIFVVVVRVVQE0/a5pmslVxr/c3FwGDBhAYWGh2aV0KzLSFnEp\nFAoxe/ZsCgsLefbZZ8nJyTG7JEtJTk6mX79+fPfdd2aXEnd2795NIBDg9ttvP+a25cuXs3z5clat\n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\n/fcf8xEnwzC48847WblyZYebdmVlJevXr+f8889vtels2LCB1atXc+DAAdLT09vdVt++fQHYtm1b\ni8gbIgcdzdfpKgMHDgQio+PMzMyT3t6rr75KaWkps2fPBmD//v0ATY/P7XaTmppKaWnpSd+XEN2F\nNG3xvaZpGm+99RajRo1i6tSpra7z1ltvsWbNGu66664OfW559erVBINBfv7zn3PWWWcdc3u/fv14\n8803WbVqFTfddBMAXq+XysrKY9adOHEiDzzwAMuXL+eMM85oGi3v3LmTd999l9GjR+Pz+aKu7URc\neOGFLFmyhD/96U+cccYZuN3uptsMw4jqvHaj8vJyHnzwQRYtWoTX6wUgLS0NgG+//Zbc3FwOHjzI\noUOHmpYLIY5PzmmL77WPPvqIsrKyNmNxiDSrqqqqqM8/N1q5ciWpqakUFBS0evuIESPIzs5u8VGr\nvLw8vvnmGx577DFWr17NmjVrgMiEs+nTp7Nx40amTZvGihUrePTRR/nZz36G3W7nt7/9bYdqOxFZ\nWVnMmzePzZs3c/nll/P444/zl7/8hQcffJALLrigQ9tatGgRY8aM4fzzz29alp+fT3Z2NvPnz+el\nl15izpw5eL1efvjDH3byIxHi+0tG2uJ7rfFz2O01nYkTJ2K321m5cmXU55+/+eYbCgsLueKKK1rM\n9j7aBRdcwLPPPsuXX35Jfn4+t912G4cPH+b555+nuroagEsuuQSA+fPn069fP1555RUWL16My+Vi\nzJgxzJkzh+HDh0f7kE/K9OnT6devH88++yzPPPMMhmGQmZnZoab9j3/8gw0bNjQdkDRyOp0sXbqU\ne+65hyVLljBgwACWLl1KampqZz8MIb63FMM46jJJQgghhIhLEo8LIYQQFiFNWwghhLAIadpCCCGE\nRUjTFkIIISxCmrYQQghhEdK0hRBCCIuQpi2EEEJYhDRtIYQQwiKkaQshhBAW8f8B+UgbjvwC5BsA\nAAAASUVORK5CYII=\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "ax = sns.jointplot(np.asarray(x), np.asarray(y), kind=\"hex\", \n",
+ " gridsize=7, size=7, stat_func=None).set_axis_labels(\"Al Atomic %\", \"Cr Atomic %\")"
+ ]
+ },
+ {
+ "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.5.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/examples/Example Machine Learning - OQMD.ipynb b/docs/examples/Example Machine Learning - OQMD.ipynb
similarity index 100%
rename from examples/Example Machine Learning - OQMD.ipynb
rename to docs/examples/Example Machine Learning - OQMD.ipynb
diff --git a/examples/Example Statistics - MDF Datasets.ipynb b/docs/examples/Example Statistics - MDF Datasets.ipynb
similarity index 100%
rename from examples/Example Statistics - MDF Datasets.ipynb
rename to docs/examples/Example Statistics - MDF Datasets.ipynb
diff --git a/examples/requirements.txt b/docs/examples/requirements.txt
similarity index 100%
rename from examples/requirements.txt
rename to docs/examples/requirements.txt
diff --git a/docs/tutorials/5 - Field-Specific Helper Functions.ipynb b/docs/tutorials/5 - Field-Specific Helper Functions.ipynb
index c1f64bc..a446c90 100644
--- a/docs/tutorials/5 - Field-Specific Helper Functions.ipynb
+++ b/docs/tutorials/5 - Field-Specific Helper Functions.ipynb
@@ -41,7 +41,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 3,
@@ -69,7 +69,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 4,
@@ -97,7 +97,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 5,
@@ -112,7 +112,9 @@
{
"cell_type": "code",
"execution_count": 6,
- "metadata": {},
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
@@ -120,29 +122,32 @@
"{'mdf': {'collection': 'OQMD',\n",
" 'composition': 'Al2Cu1',\n",
" 'elements': ['Al', 'Cu'],\n",
- " 'ingest_date': '2017-08-04T14:20:14.773902Z',\n",
- " 'links': {'landing_page': 'http://oqmd.org/analysis/calculation/59393',\n",
+ " 'ingest_date': '2017-08-04T14:19:13.058498Z',\n",
+ " 'links': {'landing_page': 'http://oqmd.org/analysis/calculation/17664',\n",
" 'metadata': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/42517/standard/metadata.json'},\n",
+ " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/107544/static/metadata.json'},\n",
" 'outcar': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/42517/standard/OUTCAR'},\n",
+ " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/107544/static/OUTCAR'},\n",
" 'parent_id': '5984824ba5ea60170af49754'},\n",
- " 'mdf_id': '5984829ea5ea60172af4da9b',\n",
+ " 'mdf_id': '59848261a5ea60172af4a8c2',\n",
" 'metadata_version': '0.3.2',\n",
" 'resource_type': 'record',\n",
- " 'scroll_id': 17223,\n",
+ " 'scroll_id': 4462,\n",
" 'source_name': 'oqmd',\n",
" 'tags': ['metadata', 'outcar'],\n",
" 'title': 'OQMD - Al2Cu1'},\n",
" 'oqmd': {'band_gap': {'units': 'eV', 'value': 0.0},\n",
+ " 'configuration': 'static',\n",
" 'converged': True,\n",
- " 'crossreference': {'icsd': 42517},\n",
+ " 'crossreference': {'icsd': 107544},\n",
+ " 'delta_e': {'units': 'eV/atom', 'value': -0.15628721},\n",
" 'magnetic_moment': {'units': 'bohr/atom'},\n",
- " 'spacegroup': 140,\n",
- " 'total_energy': {'units': 'eV/atom', 'value': -3.88954765666667},\n",
- " 'volume': {'units': 'angstrom^3/atom', 'value': 14.5666}}}"
+ " 'spacegroup': 69,\n",
+ " 'stability': {'units': 'eV/atom', 'value': 0.018707923333333},\n",
+ " 'total_energy': {'units': 'eV/atom', 'value': -3.89209998333333},\n",
+ " 'volume': {'units': 'angstrom^3/atom', 'value': 14.7911}}}"
]
},
"execution_count": 6,
@@ -171,7 +176,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 7,
@@ -187,37 +192,42 @@
{
"cell_type": "code",
"execution_count": 8,
- "metadata": {},
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
"text/plain": [
"[{'mdf': {'collection': 'OQMD',\n",
- " 'composition': 'Al3Cu1',\n",
+ " 'composition': 'Al1Cu3',\n",
" 'elements': ['Al', 'Cu'],\n",
- " 'ingest_date': '2017-08-04T14:24:39.255975Z',\n",
- " 'links': {'landing_page': 'http://oqmd.org/analysis/calculation/214708',\n",
+ " 'ingest_date': '2017-08-04T14:19:02.238752Z',\n",
+ " 'links': {'landing_page': 'http://oqmd.org/analysis/calculation/8950',\n",
" 'metadata': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/oqmd/data/home/oqmd/libraries/prototypes/binaries/D0_3/Al_Cu/standard/metadata.json'},\n",
+ " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/150823/static/metadata.json'},\n",
" 'outcar': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/oqmd/data/home/oqmd/libraries/prototypes/binaries/D0_3/Al_Cu/standard/OUTCAR'},\n",
+ " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/150823/static/OUTCAR'},\n",
" 'parent_id': '5984824ba5ea60170af49754'},\n",
- " 'mdf_id': '598483a7a5ea60172af58f7f',\n",
+ " 'mdf_id': '59848256a5ea60172af49f39',\n",
" 'metadata_version': '0.3.2',\n",
" 'resource_type': 'record',\n",
- " 'scroll_id': 63531,\n",
+ " 'scroll_id': 2021,\n",
" 'source_name': 'oqmd',\n",
" 'tags': ['metadata', 'outcar'],\n",
- " 'title': 'OQMD - Al3Cu1'},\n",
+ " 'title': 'OQMD - Al1Cu3'},\n",
" 'oqmd': {'band_gap': {'units': 'eV', 'value': 0.0},\n",
- " 'configuration': 'standard',\n",
+ " 'configuration': 'static',\n",
" 'converged': True,\n",
+ " 'crossreference': {'icsd': 150823},\n",
+ " 'delta_e': {'units': 'eV/atom', 'value': -0.1675233825},\n",
" 'magnetic_moment': {'units': 'bohr/atom'},\n",
" 'spacegroup': 225,\n",
- " 'total_energy': {'units': 'eV/atom', 'value': -3.6518156475},\n",
- " 'volume': {'units': 'angstrom^3/atom', 'value': 15.3631}}}]"
+ " 'stability': {'units': 'eV/atom', 'value': 0.02138741875},\n",
+ " 'total_energy': {'units': 'eV/atom', 'value': -3.8909277975},\n",
+ " 'volume': {'units': 'angstrom^3/atom', 'value': 12.3364}}}]"
]
},
"execution_count": 8,
@@ -249,7 +259,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 9,
@@ -264,7 +274,9 @@
{
"cell_type": "code",
"execution_count": 10,
- "metadata": {},
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
@@ -339,7 +351,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 11,
@@ -354,45 +366,54 @@
{
"cell_type": "code",
"execution_count": 12,
- "metadata": {},
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
"text/plain": [
- "{'mdf': {'author': [{'email': 'c-wolverton@northwestern.edu',\n",
- " 'family_name': 'Wolverton',\n",
- " 'full_name': 'Chris Wolverton',\n",
- " 'given_name': 'Chris',\n",
- " 'institution': 'Northwestern University'},\n",
- " {'family_name': 'Doak',\n",
- " 'full_name': 'Jeff Doak',\n",
- " 'given_name': 'Jeff',\n",
- " 'institution': 'Northwestern University'}],\n",
- " 'citation': ['Doak JW, Wolverton C (2012) Coherent and incoherent phase stabilities of thermoelectric rocksalt IV-VI semiconductor alloys. Phys. Rev. B 86: 144202 http://dx.doi.org/10.1103/PhysRevB.86.144202'],\n",
- " 'collection': 'Doak Strain Energies',\n",
- " 'data_contact': {'email': 'c-wolverton@northwestern.edu',\n",
- " 'family_name': 'Wolverton',\n",
- " 'full_name': 'Chris Wolverton',\n",
- " 'given_name': 'Chris',\n",
- " 'institution': 'Northwestern University'},\n",
+ "{'mdf': {'author': [{'email': 'mfelling@illinois.edu',\n",
+ " 'family_name': 'Fellinger',\n",
+ " 'full_name': 'Michael Fellinger',\n",
+ " 'given_name': 'Michael',\n",
+ " 'institution': 'University of Illinois'},\n",
+ " {'family_name': 'Trinkle',\n",
+ " 'full_name': 'Dallas Trinkle',\n",
+ " 'given_name': 'Dallas',\n",
+ " 'institution': 'University of Illinois'},\n",
+ " {'family_name': 'Hector Jr.',\n",
+ " 'full_name': 'Louis Hector Jr.',\n",
+ " 'given_name': 'Louis',\n",
+ " 'institution': 'General Motors'}],\n",
+ " 'citation': ['M. R. Fellinger, L. G. Hector Jr., and D. R. Trinkle, Comp. Mat. Sci. 126, 503 (2017).',\n",
+ " 'M. R. Fellinger, L. G. Hector Jr., and D. R. Trinkle, Data in Brief 10, 147 (2017).'],\n",
+ " 'collection': 'Elastic Fe BCC',\n",
+ " 'data_contact': {'email': 'mfelling@illinois.edu',\n",
+ " 'family_name': 'Fellinger',\n",
+ " 'full_name': 'Michael Fellinger',\n",
+ " 'given_name': 'Michael',\n",
+ " 'institution': 'University of Illinois'},\n",
" 'data_contributor': [{'email': 'jgaff@uchicago.edu',\n",
" 'family_name': 'Gaff',\n",
" 'full_name': 'Jonathon Gaff',\n",
" 'github': 'jgaff',\n",
" 'given_name': 'Jonathon',\n",
" 'institution': 'The University of Chicago'}],\n",
- " 'description': 'We use density functional theory calculations to investigate the coherent and incoherent phase stability of the IV–VI rocksalt semiconductor alloy systems Pb(S,Te), Pb(Te,Se), Pb(Se,S), (Pb,Sn)Te, (Sn,Ge)Te, and (Ge,Pb)Te.',\n",
- " 'ingest_date': '2017-08-04T19:21:29.013385Z',\n",
- " 'links': {'data_doi': 'http://hdl.handle.net/11256/85',\n",
- " 'landing_page': 'https://materialsdata.nist.gov/dspace/xmlui/handle/11256/85',\n",
- " 'publication': ['http://dx.doi.org/10.1103/PhysRevB.86.144202']},\n",
- " 'mdf_id': '5984c939f2c00437dc2502a5',\n",
+ " 'description': 'We introduce a solute strain misfit tensor that quantifies how solutes change the lattice parameter.',\n",
+ " 'ingest_date': '2017-08-04T19:51:21.076837Z',\n",
+ " 'license': 'http://creativecommons.org/publicdomain/zero/1.0/',\n",
+ " 'links': {'data_doi': 'http://hdl.handle.net/11256/671',\n",
+ " 'landing_page': 'https://materialsdata.nist.gov/dspace/xmlui/handle/11256/671',\n",
+ " 'publication': ['http://dx.doi.org/10.1016/j.commatsci.2016.09.040',\n",
+ " 'http://dx.doi.org/10.1016/j.dib.2016.11.092']},\n",
+ " 'mdf_id': '5984d039f2c0043894245abb',\n",
" 'metadata_version': '0.3.2',\n",
" 'resource_type': 'dataset',\n",
- " 'source_name': 'doak_strain_energies',\n",
+ " 'source_name': 'trinkle_elastic_fe_bcc',\n",
" 'tags': ['dft'],\n",
- " 'title': 'GeTe-PbTe PbS-PbTe PbSe-PbS PbTe-PbSe PbTe-SnTe SnTe-GeTe mixing and coherency strain energies',\n",
- " 'year': 2012}}"
+ " 'title': 'Ab initio calculations of the lattice parameter and elastic stiffness coefficients of bcc Fe with solutes',\n",
+ " 'year': 2017}}"
]
},
"execution_count": 12,
@@ -405,6 +426,157 @@
"res[0]"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### match_years\n",
+ "`match_years()` matches values against the `\"mdf.year\"` field."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "mdf.match_years([\"2015\", 2010])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'mdf': {'collection': 'PPPDB',\n",
+ " 'composition': 'C8H8 C7H7N',\n",
+ " 'description': 'Chi Parameter for poly(styrene) and poly(4-vinylpyridine)',\n",
+ " 'elements': ['H', 'N', 'C'],\n",
+ " 'ingest_date': '2017-08-04T20:56:39.045371Z',\n",
+ " 'links': {'landing_page': 'http://pppdb.uchicago.edu?&id=164',\n",
+ " 'parent_id': '5984df58f2c004392664b14c',\n",
+ " 'publication': ['10.1021/ma1006792']},\n",
+ " 'mdf_id': '5984df87f2c004392664b15e',\n",
+ " 'metadata_version': '0.3.2',\n",
+ " 'raw': '{\"doi\": \"10.1021/ma1006792\", \"type\": \"Type 1\", \"temperature\": 160.0, \"tempunit\": \"Celsius\", \"chinumber\": 0.3, \"chierror\": 0.0, \"chia\": 0.0, \"chiaerror\": 0.0, \"chib\": 0.0, \"chiberror\": 0.0, \"chic\": 0.0, \"chicerror\": 0.0, \"notes\": \"\", \"indirect\": 1, \"reference\": \"DOI: 10.1021/ma062516u; DOI: 10.1021/ma702780c\", \"compound1\": \"poly(styrene)\", \"compound2\": \"poly(4-vinylpyridine)\", \"authors\": \"Biplab K. Kuila, E. Bhoje Gowd, and Manfred Stamm\", \"date\": \"August 19, 2010\", \"pppdb_id\": 164}',\n",
+ " 'resource_type': 'record',\n",
+ " 'scroll_id': 18,\n",
+ " 'source_name': 'pppdb',\n",
+ " 'tags': ['poly(styrene)', 'poly(4-vinylpyridine)'],\n",
+ " 'title': 'PPPDB - Chi Parameter for poly(styrene) and poly(4-vinylpyridine)',\n",
+ " 'year': 2010},\n",
+ " 'pppdb': {'authors': 'Biplab K. Kuila, E. Bhoje Gowd, and Manfred Stamm',\n",
+ " 'chierror': 0.0,\n",
+ " 'chinumber': 0.3,\n",
+ " 'date': 'August 19, 2010',\n",
+ " 'id': 164,\n",
+ " 'reference': 'DOI: 10.1021/ma062516u; DOI: 10.1021/ma702780c',\n",
+ " 'temperature': 160.0,\n",
+ " 'tempunit': 'Celsius',\n",
+ " 'type': 'Type 1'}}"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "res = mdf.search(limit=10)\n",
+ "res[0]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can also specify a range of years."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "mdf.match_years(start=2014, stop=2016, inclusive=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'mdf': {'collection': 'PPPDB',\n",
+ " 'composition': 'C7H7N C7H8',\n",
+ " 'description': 'could not access cited reference for method',\n",
+ " 'elements': ['H', 'N', 'C'],\n",
+ " 'ingest_date': '2017-08-04T21:06:57.080792Z',\n",
+ " 'links': {'landing_page': 'http://pppdb.uchicago.edu?&id=606',\n",
+ " 'parent_id': '5984df58f2c004392664b14c',\n",
+ " 'publication': ['10.1021/acs.macromol.5b01261']},\n",
+ " 'mdf_id': '5984e1f1f2c004392664b2cc',\n",
+ " 'metadata_version': '0.3.2',\n",
+ " 'raw': '{\"doi\": \"10.1021/acs.macromol.5b01261\", \"type\": \"Type 1\", \"temperature\": 0.0, \"tempunit\": \"\", \"chinumber\": 1.01, \"chierror\": 0.0, \"chia\": 0.0, \"chiaerror\": 0.0, \"chib\": 0.0, \"chiberror\": 0.0, \"chic\": 0.0, \"chicerror\": 0.0, \"notes\": \"could not access cited reference for method\", \"indirect\": 1, \"reference\": \"ISBN: 0-471-16628-6\", \"compound1\": \"poly(4-vinylpyridine)\", \"compound2\": \"toluene\", \"authors\": \"Renhua Deng, Hui Li, Fuxin Liang, Jintao Zhu, Baohui Li, Xiaolin Xie, and Zhenzhong Yang \", \"date\": \"August 6, 2015\", \"pppdb_id\": 606}',\n",
+ " 'resource_type': 'record',\n",
+ " 'scroll_id': 384,\n",
+ " 'source_name': 'pppdb',\n",
+ " 'tags': ['poly(4-vinylpyridine)', 'toluene'],\n",
+ " 'title': 'PPPDB - Chi Parameter for poly(4-vinylpyridine) and toluene',\n",
+ " 'year': 2015},\n",
+ " 'pppdb': {'authors': 'Renhua Deng, Hui Li, Fuxin Liang, Jintao Zhu, Baohui Li, Xiaolin Xie, and Zhenzhong Yang ',\n",
+ " 'chierror': 0.0,\n",
+ " 'chinumber': 1.01,\n",
+ " 'date': 'August 6, 2015',\n",
+ " 'id': 606,\n",
+ " 'reference': 'ISBN: 0-471-16628-6',\n",
+ " 'temperature': 0.0,\n",
+ " 'tempunit': '',\n",
+ " 'type': 'Type 1'}}"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "res = mdf.search(limit=10)\n",
+ "res[0]"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -422,7 +594,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 17,
"metadata": {
"scrolled": true
},
@@ -431,34 +603,37 @@
"data": {
"text/plain": [
"{'mdf': {'collection': 'OQMD',\n",
- " 'composition': 'Al3Cu1',\n",
+ " 'composition': 'Al2Cu1',\n",
" 'elements': ['Al', 'Cu'],\n",
- " 'ingest_date': '2017-08-04T14:24:39.255975Z',\n",
- " 'links': {'landing_page': 'http://oqmd.org/analysis/calculation/214708',\n",
+ " 'ingest_date': '2017-08-04T14:19:13.058498Z',\n",
+ " 'links': {'landing_page': 'http://oqmd.org/analysis/calculation/17664',\n",
" 'metadata': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/oqmd/data/home/oqmd/libraries/prototypes/binaries/D0_3/Al_Cu/standard/metadata.json'},\n",
+ " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/107544/static/metadata.json'},\n",
" 'outcar': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/oqmd/data/home/oqmd/libraries/prototypes/binaries/D0_3/Al_Cu/standard/OUTCAR'},\n",
+ " 'path': '/collections/oqmd/data/home/oqmd/libraries/icsd/107544/static/OUTCAR'},\n",
" 'parent_id': '5984824ba5ea60170af49754'},\n",
- " 'mdf_id': '598483a7a5ea60172af58f7f',\n",
+ " 'mdf_id': '59848261a5ea60172af4a8c2',\n",
" 'metadata_version': '0.3.2',\n",
" 'resource_type': 'record',\n",
- " 'scroll_id': 63531,\n",
+ " 'scroll_id': 4462,\n",
" 'source_name': 'oqmd',\n",
" 'tags': ['metadata', 'outcar'],\n",
- " 'title': 'OQMD - Al3Cu1'},\n",
+ " 'title': 'OQMD - Al2Cu1'},\n",
" 'oqmd': {'band_gap': {'units': 'eV', 'value': 0.0},\n",
- " 'configuration': 'standard',\n",
+ " 'configuration': 'static',\n",
" 'converged': True,\n",
+ " 'crossreference': {'icsd': 107544},\n",
+ " 'delta_e': {'units': 'eV/atom', 'value': -0.15628721},\n",
" 'magnetic_moment': {'units': 'bohr/atom'},\n",
- " 'spacegroup': 225,\n",
- " 'total_energy': {'units': 'eV/atom', 'value': -3.6518156475},\n",
- " 'volume': {'units': 'angstrom^3/atom', 'value': 15.3631}}}"
+ " 'spacegroup': 69,\n",
+ " 'stability': {'units': 'eV/atom', 'value': 0.018707923333333},\n",
+ " 'total_energy': {'units': 'eV/atom', 'value': -3.89209998333333},\n",
+ " 'volume': {'units': 'angstrom^3/atom', 'value': 14.7911}}}"
]
},
- "execution_count": 13,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -478,8 +653,10 @@
},
{
"cell_type": "code",
- "execution_count": 14,
- "metadata": {},
+ "execution_count": 18,
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
@@ -514,7 +691,7 @@
" 'year': 1985}}"
]
},
- "execution_count": 14,
+ "execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@@ -534,8 +711,10 @@
},
{
"cell_type": "code",
- "execution_count": 15,
- "metadata": {},
+ "execution_count": 19,
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"data": {
@@ -580,7 +759,7 @@
" 'year': 2014}}"
]
},
- "execution_count": 15,
+ "execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@@ -600,42 +779,44 @@
},
{
"cell_type": "code",
- "execution_count": 16,
- "metadata": {},
+ "execution_count": 20,
+ "metadata": {
+ "scrolled": true
+ },
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
- "100%|██████████| 1246/1246 [00:01<00:00, 669.40it/s]\n"
+ "1401it [00:01, 787.20it/s] \n"
]
},
{
"data": {
"text/plain": [
- "{'fe_cr_al_oxidation': {'atomic_composition_percent': {'Al': 4.5,\n",
- " 'Cr': 25.7,\n",
- " 'Fe': 69.8},\n",
+ "{'fe_cr_al_oxidation': {'atomic_composition_percent': {'Al': 3.1,\n",
+ " 'Cr': 26.5,\n",
+ " 'Fe': 70.5},\n",
" 'temperature_k': 420.0},\n",
" 'mdf': {'collection': 'Fe-Cr-Al Oxidation Studies',\n",
" 'composition': 'FeCrAl',\n",
" 'elements': ['Cr', 'Fe', 'Al'],\n",
- " 'ingest_date': '2017-08-04T21:26:53.266642Z',\n",
+ " 'ingest_date': '2017-08-04T21:26:53.296946Z',\n",
" 'links': {'csv': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
" 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/Fe_Cr_Al_data/420 K/420 K Point 100.txt'},\n",
- " 'landing_page': 'https://materialsdata.nist.gov/dspace/xmlui/handle/11256/836#100',\n",
+ " 'path': '/collections/Fe_Cr_Al_data/420 K/420 K Point 104.txt'},\n",
+ " 'landing_page': 'https://materialsdata.nist.gov/dspace/xmlui/handle/11256/836#104',\n",
" 'parent_id': '5984e69cf2c00439c790bf54'},\n",
- " 'mdf_id': '5984e69df2c00439c790bfb8',\n",
+ " 'mdf_id': '5984e69df2c00439c790bfbc',\n",
" 'metadata_version': '0.3.2',\n",
" 'resource_type': 'record',\n",
- " 'scroll_id': 100,\n",
+ " 'scroll_id': 104,\n",
" 'source_name': 'fe_cr_al_oxidation',\n",
" 'tags': ['csv'],\n",
- " 'title': 'Fe-Cr-Al Oxidation - 420 K Point 100'}}"
+ " 'title': 'Fe-Cr-Al Oxidation - 420 K Point 104'}}"
]
},
- "execution_count": 16,
+ "execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
diff --git a/docs/tutorials/6 - Data Retrieval Functions.ipynb b/docs/tutorials/6 - Data Retrieval Functions.ipynb
index 3ea71be..4910ab3 100644
--- a/docs/tutorials/6 - Data Retrieval Functions.ipynb
+++ b/docs/tutorials/6 - Data Retrieval Functions.ipynb
@@ -152,36 +152,6 @@
"next(raw_data)"
]
},
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### http_return\n",
- "To fetch all the data all at once in one big list, use `http_return()`. This function is equivalent to `list(http_stream())`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'CHEMBL1200572\\n SciTegic12231509382D\\n\\n 2 1 0 0 0 0 999 V2000\\n 4.8167 -4.8250 0.0000 Mg 0 0\\n 5.5292 -4.8250 0.0000 O 0 0\\n 2 1 2 0\\nM END\\n> \\nCHEMBL1200572\\n\\n'"
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "res = mdf.search(\"MgO\", limit=1)\n",
- "raw_data = mdf.http_return(res)\n",
- "raw_data[0]"
- ]
- },
{
"cell_type": "code",
"execution_count": null,
diff --git a/examples/Example Analysis - Fe-Cr-Al Oxidation.ipynb b/examples/Example Analysis - Fe-Cr-Al Oxidation.ipynb
deleted file mode 100644
index 17bb81a..0000000
--- a/examples/Example Analysis - Fe-Cr-Al Oxidation.ipynb
+++ /dev/null
@@ -1,291 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Example Analysis\n",
- "\n",
- "### A high-throughput investigation of Fe–Cr–Al as a novel high-temperature coating for nuclear cladding materials\n",
- "\n",
- "Article Link\n",
- "\n",
- "\"Bunn, Jonathan Kenneth, Randy L. Fang, Mark R. Albing, Apurva Mehta, Matthew J. Kramer, Matthew F. Besser, and Jason R. Hattrick-Simpers. \"A high-throughput investigation of Fe–Cr–Al as a novel high-temperature coating for nuclear cladding materials.\" Nanotechnology 26, no. 27 (2015): 274003.\"\n",
- "\n",
- "\n",
- "Example: We want to plot some of the data from the above study using MDF.\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from io import StringIO\n",
- "from multiprocessing.pool import Pool\n",
- "\n",
- "from mdf_forge.forge import Forge\n",
- "\n",
- "import matplotlib.pyplot as plt\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "import seaborn as sns\n",
- "\n",
- "%matplotlib inline\n",
- "sns.set_context('poster')\n",
- "sns.set_style('white')"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Retrieve Records"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Found 1401 matches\n"
- ]
- }
- ],
- "source": [
- "mdf = Forge()\n",
- "res = mdf.match_field(\"mdf.source_name\",\"fe_cr_al_oxidation\").match_field(\"mdf.resource_type\", \"record\").search()\n",
- "print(\"Found {results} matches\".format(results=len(res)))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "{'fe_cr_al_oxidation': {'atomic_composition_percent': {'Al': 3.1,\n",
- " 'Cr': 26.5,\n",
- " 'Fe': 70.5},\n",
- " 'temperature_k': 420.0},\n",
- " 'mdf': {'collection': 'Fe-Cr-Al Oxidation Studies',\n",
- " 'composition': 'FeCrAl',\n",
- " 'elements': ['Cr', 'Fe', 'Al'],\n",
- " 'ingest_date': '2017-08-04T21:26:53.296946Z',\n",
- " 'links': {'csv': {'globus_endpoint': '82f1b5c6-6e9b-11e5-ba47-22000b92c6ec',\n",
- " 'http_host': 'https://data.materialsdatafacility.org',\n",
- " 'path': '/collections/Fe_Cr_Al_data/420 K/420 K Point 104.txt'},\n",
- " 'landing_page': 'https://materialsdata.nist.gov/dspace/xmlui/handle/11256/836#104',\n",
- " 'parent_id': '5984e69cf2c00439c790bf54'},\n",
- " 'mdf_id': '5984e69df2c00439c790bfbc',\n",
- " 'metadata_version': '0.3.2',\n",
- " 'resource_type': 'record',\n",
- " 'scroll_id': 104,\n",
- " 'source_name': 'fe_cr_al_oxidation',\n",
- " 'tags': ['csv'],\n",
- " 'title': 'Fe-Cr-Al Oxidation - 420 K Point 104'}}"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "res[0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Download data using HTTP"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "def format_get_cr_al_params(d):\n",
- " tmp_d = {}\n",
- " for key in d['atomic_composition_percent']:\n",
- " tmp_d[key] = float(d['atomic_composition_percent'][key])\n",
- " tmp_d['temperature_k'] = float(d['temperature_k']) if d['temperature_k'] != \"Room\" else 297.15 # Avg room temp\n",
- " return tmp_d\n",
- " \n",
- "def get_fe_cr_al(r):\n",
- " res = mdf.http_return(r)[0]\n",
- " \n",
- " params = format_get_cr_al_params(r['fe_cr_al_oxidation']) \n",
- " \n",
- " df = pd.read_csv(StringIO(res), sep=\"\\t\", header=None, names=[\"twotheta\",\"counts\"])\n",
- " return (params, df)\n",
- "\n",
- "n_workers = 10\n",
- "n_points = 300\n",
- "\n",
- "mp = Pool(n_workers)\n",
- "mdf_data = mp.map(get_fe_cr_al, res[:n_points])\n",
- "mp.close()\n",
- "mp.join()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Aggregate Results"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "## Choose peak integration range (this is ~ a range for the Fe3O4 oxide)\n",
- "\n",
- "integration_peak = 42.8\n",
- "integration_width = 1.\n",
- "integration_range = (integration_peak-integration_width,integration_peak+integration_width)\n",
- "\n",
- "results = []\n",
- "\n",
- "for data, df_tmp in mdf_data:\n",
- " result = {\"data\":{},\"aggregation\":0}\n",
- " result['data'] = data\n",
- " agg = df_tmp[(df_tmp.twotheta>integration_range[0]) & \n",
- " (df_tmp.twotheta"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "x = [r[\"data\"][\"Al\"] for r in results]\n",
- "y = [r[\"data\"][\"Cr\"] for r in results]\n",
- "s = [[r[\"aggregation_norm\"]*1000 for r in results]]\n",
- "\n",
- "fig, ax = plt.subplots()\n",
- "plt.scatter(x, y, s=s, alpha=0.6)\n",
- "ax.set_xlabel(\"Al Atomic %\")\n",
- "ax.set_ylabel(\"Cr Atomic %\")\n",
- "sns.despine()\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Joint Plots (Where we have observations)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "ax = sns.jointplot(np.asarray(x), np.asarray(y), kind=\"kde\", \n",
- " shade=True, stat_func=None, size=7).set_axis_labels(\"Al Atomic %\", \"Cr Atomic %\")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "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\nz19eDAfKmdByeFQEUSF4K/s8LfsOPdHIMOSVtlIKH374IXbu3ImxY8dizpw50HVv7e0SERF5yZBC\ne/v27bj88stRX1/ffdmUKVNw3333Ydq0aXkbjoiIiL40pHr81ltvxZQpU/DHP/4RH330EZ555hkE\ng0Hccsst+Z6PiIiIuvQb2k8//XSvl3/yySdYsmQJJk+ejEAggFmzZuH888/H+vXrCzIkERERDRDa\n99xzDy688EJs2rRpn8sPOuggPPXUU8hkMgCAtrY2vPTSS5gyZUrhJiUiIhrh+g3tl156CYceeij+\n4R/+AXfffXd3SP/oRz/CK6+8gmOPPRbz58/H/PnzsWHDBtx4441FGZqIiGgk6vdAtNLSUtx00034\nu7/7O9xyyy148cUXccstt2D+/Pn44x//iNWrV6O5uRljx47FySefjMrKymLNTURENOLYOnr88MMP\nx9NPP43HHnsM3/ve93DSSSfhxhtvxJlnnlno+YiIiKiL7aPHNU3Dt7/9bbz00kuwLAunn346Hn/8\ncSjFs0gQEREVw4Ch3dTUhKeeegqPPvooPvroI4wfPx733HMP7rjjDixfvhznnHMOPv3002LMSkRE\nNKL1W4+vWbMGV111FQAgGAwiFovhu9/9Lq655hqcfPLJmDdvHu69916cf/75OP/883H11VcjHA4X\nZXAiIqKRpt+V9h133IG5c+di7dq1WLt2La699lo88MAD2L17NwAgFArhuuuuw+9+9zt89NFHOP30\n04syNBER0UjUb2hv27YNCxYsQDAYBAB8/etfh5QS27dv3+d2M2bMwBNPPIErr7yycJMSERGNcP2G\ndl1dHVauXInm5mbE43E89thj8Pv9mDp1aq+3P+eccwoxIxEREWGA97RvuukmXHHFFTj55JMBALqu\n44YbbkBFRUUxZiMiIqK99Bvas2fPxv/+7//i/fffRzqdxqxZszBhwoRizUZERER7GfDkKqWlpZg/\nf34xZiEiIqJ+DOmrOYmIiKj4GNpEREQewdAmIiLyCIY2ERGRRzC0iYiIPMLWV3MWypo1a/DAAw9g\n06ZNiEajqKqqwpFHHomrrroKdXV13bfbsWMHbrvtNrzxxhtQSuGEE07Aj370I9TU1Dg4PRERUXHZ\nXmk/8MADOO+88/q8/vzzz8dDDz00qAePRqOYNWsWbr75Zixfvhz/9m//hvr6epx77rndp0pNJpP4\n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\n",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "ax = sns.jointplot(np.asarray(x), np.asarray(y), kind=\"hex\", \n",
- " gridsize=7, size=7, stat_func=None).set_axis_labels(\"Al Atomic %\", \"Cr Atomic %\")"
- ]
- },
- {
- "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.5.2"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/mdf_forge/forge.py b/mdf_forge/forge.py
index b35deb7..daf218d 100644
--- a/mdf_forge/forge.py
+++ b/mdf_forge/forge.py
@@ -23,57 +23,63 @@ class Forge:
Public Variables:
local_ep is the endpoint ID of the local Globus Connect Personal endpoint.
-
+ index is the Globus Search index to be used.
"""
__default_index = "mdf"
__services = ["mdf", "transfer", "search"]
__app_name = "MDF_Forge"
- def __init__(self, index=__default_index, local_ep=None, **kwargs):
+ def __init__(self, index=__default_index, local_ep=None, anonymous=False, **kwargs):
"""Initialize the Forge instance.
Arguments:
index (str): The Globus Search index to search on. Default "mdf".
local_ep (str): The endpoint ID of the local Globus Connect Personal endpoint.
If not provided, may be autodetected as possible.
+ anonymous (bool): If True, will not authenticate with Globus Auth.
+ If False, will require authentication.
+ Please note that authentication is required for some Forge
+ functionality, including using Globus Transfer.
Keyword Arguments:
services (list of str): The services to authenticate for.
Advanced users only.
"""
- self.__index = index
+ self.__anonymous = anonymous
+ self.index = index
self.local_ep = local_ep
- self.__services = kwargs.get('services', self.__services)
- clients = toolbox.login(credentials={
- "app_name": self.__app_name,
- "services": self.__services,
- "index": self.__index})
- self.__search_client = clients["search"]
- self.__transfer_client = clients["transfer"]
- self.__mdf_authorizer = clients["mdf"]
+ services = kwargs.get('services', self.__services)
- self.__query = Query(self.__search_client)
+ if self.__anonymous:
+ clients = toolbox.anonymous_login(services)
+ else:
+ clients = toolbox.login(credentials={
+ "app_name": self.__app_name,
+ "services": services,
+ "index": self.index})
+ self.__search_client = clients.get("search")
+ self.__transfer_client = clients.get("transfer")
+ self.__mdf_authorizer = clients.get("mdf")
+ self.__query = Query(self.__search_client)
@property
def search_client(self):
return self.__search_client
-
@property
def transfer_client(self):
return self.__transfer_client
-
@property
def mdf_authorizer(self):
return self.__mdf_authorizer
-#################################################
-## Core functions
-#################################################
+# ***********************************************
+# * Core functions
+# ***********************************************
def match_field(self, field, value, required=True, new_group=False):
"""Add a field:value term to the query.
@@ -98,10 +104,9 @@ def match_field(self, field, value, required=True, new_group=False):
self.__query.and_join(new_group)
else:
self.__query.or_join(new_group)
- self.__query.field(field, value)
+ self.__query.field(str(field), str(value))
return self
-
def exclude_field(self, field, value, new_group=False):
"""Exclude a field:value term from the query.
Matches will NOT have field == value.
@@ -122,21 +127,21 @@ def exclude_field(self, field, value, new_group=False):
# OR would not make much sense for excluding
if self.__query.initialized:
self.__query.and_join(new_group)
- self.__query.negate().field(field, value)
+ self.__query.negate().field(str(field), str(value))
return self
-
- def search(self, q=None, advanced=False, limit=SEARCH_LIMIT, info=False,
+ def search(self, q=None, index=None, advanced=False, limit=SEARCH_LIMIT, info=False,
reset_query=True):
"""Execute a search and return the results.
Arguments:
q (str): The query to execute. Defaults to the current query, if any.
There must be some query to execute.
+ index (str): The Globus Search index to search on. Defaults to the current index.
advanced (bool): If True, will submit query in "advanced" mode to enable field matches.
If False, only basic fulltext term matches will be supported.
Default False.
- This value will change to True automatically
+ This value will change to True automatically
if the query is built with helpers.
limit (int): The maximum number of results to return.
The max for this argument is the SEARCH_LIMIT imposed by Globus Search.
@@ -152,13 +157,14 @@ def search(self, q=None, advanced=False, limit=SEARCH_LIMIT, info=False,
list (if info=False): The results.
tuple (if info=True): The results, and a dictionary of query information.
"""
- res = self.__query.search(q=q, advanced=advanced, limit=limit, info=info)
+ if not index:
+ index = self.index
+ res = self.__query.search(q=q, index=index, advanced=advanced, limit=limit, info=info)
if reset_query:
self.reset_query()
return res
-
- def aggregate(self, q=None, scroll_size=SEARCH_LIMIT, reset_query=True):
+ def aggregate(self, q=None, index=None, scroll_size=SEARCH_LIMIT, reset_query=True):
"""Perform an advanced query, and return all matching results.
Will automatically preform multiple queries in order to retrieve all results.
@@ -167,6 +173,7 @@ def aggregate(self, q=None, scroll_size=SEARCH_LIMIT, reset_query=True):
Arguments:
q (str): The query to execute. Defaults to the current query, if any.
There must be some query to execute.
+ index (str): The Globus Search index to search on. Defaults to the current index.
scroll_size (int): Minimum number of records returned per query
reset_query (bool): If True, will destroy the query after execution and start a fresh one.
If False, will keep the current query alive.
@@ -175,23 +182,27 @@ def aggregate(self, q=None, scroll_size=SEARCH_LIMIT, reset_query=True):
Returns:
list of dict: All matching records
"""
- res = self.__query.aggregate(q=q, scroll_size=scroll_size)
+ if not index:
+ index = self.index
+ res = self.__query.aggregate(q=q, index=index, scroll_size=scroll_size)
if reset_query:
self.reset_query()
return res
-
- def show_fields(self, block=None):
+ def show_fields(self, block=None, index=None):
"""Retrieve and return the mapping for the given metadata block.
Arguments:
block (str): The top-level field to fetch the mapping for.
Default None, which lists just the blocks.
+ index (str): The Globus Search index to map. Defaults to the current index.
Returns:
dict: A set of field:datatype pairs.
"""
- mapping = self.__query.mapping()
+ if not index:
+ index = self.index
+ mapping = self.__query.mapping(index=index)
if not block:
blocks = set()
for key in mapping.keys():
@@ -206,7 +217,6 @@ def show_fields(self, block=None):
block_map[key] = value
return block_map
-
def current_query(self):
"""Return the current query string.
@@ -215,10 +225,9 @@ def current_query(self):
"""
return self.__query.clean_query()
-
def reset_query(self):
"""Destroy the current query and create a fresh one.
-
+
Returns:
None: Does not return self because this method should not be chained.
"""
@@ -226,11 +235,12 @@ def reset_query(self):
self.__query = Query(self.__search_client)
-#################################################
-## Expanded functions
-#################################################
+# ***********************************************
+# * Expanded functions
+# ***********************************************
- def match_range(self, field, start, stop, inclusive=True, required=True, new_group=False):
+ def match_range(self, field, start="*", stop="*", inclusive=True,
+ required=True, new_group=False):
"""Add a field:[some range] term to the query.
Matches will have field == value in range.
@@ -250,6 +260,15 @@ def match_range(self, field, start, stop, inclusive=True, required=True, new_gro
Returns:
self (Forge): For chaining.
"""
+ # Accept None as *
+ if start is None:
+ start = "*"
+ if stop is None:
+ stop = "*"
+ # No-op on *-*
+ if start == "*" and stop == "*":
+ return self
+
if inclusive:
value = "[" + str(start) + " TO " + str(stop) + "]"
else:
@@ -257,8 +276,8 @@ def match_range(self, field, start, stop, inclusive=True, required=True, new_gro
self.match_field(field, value, required=required, new_group=new_group)
return self
-
- def exclude_range(self, field, start, stop, inclusive=True, new_group=False):
+ def exclude_range(self, field, start="*", stop="*", inclusive=True,
+ required=True, new_group=False):
"""Exclude a field:[some range] term to the query.
Matches will have field != values in range.
@@ -277,6 +296,15 @@ def exclude_range(self, field, start, stop, inclusive=True, new_group=False):
Returns:
self (Forge): For chaining.
"""
+ # Accept None as *
+ if start is None:
+ start = "*"
+ if stop is None:
+ stop = "*"
+ # No-op on *-*
+ if start == "*" and stop == "*":
+ return self
+
if inclusive:
value = "[" + str(start) + " TO " + str(stop) + "]"
else:
@@ -285,13 +313,13 @@ def exclude_range(self, field, start, stop, inclusive=True, new_group=False):
return self
-#################################################
-## Helper functions
-#################################################
+# ***********************************************
+# * Helper functions
+# ***********************************************
def exclusive_match(self, field, value):
"""Match exactly the given value, with no other data in the field.
-
+
Arguments:
field (str): The field to check for the value.
The field must be namespaced according to Elasticsearch rules using the dot syntax.
@@ -307,11 +335,11 @@ def exclusive_match(self, field, value):
# Hacky way to get ES to do exclusive search
# Essentially have a big range search that matches NOT anything
# Except for the actual values
- # Example: [foo, bar, baz] =>
+ # Example: [foo, bar, baz] =>
# (NOT {* TO foo} AND [foo TO foo] AND NOT {foo to bar} AND [bar TO bar]
# AND NOT {bar TO baz} AND [baz TO baz] AND NOT {baz TO *})
# Except it must be sorted to not overlap
-
+
# Start with removing everything before first value
self.exclude_range(field, "*", value[0], inclusive=False, new_group=True)
# Select first value
@@ -325,13 +353,12 @@ def exclusive_match(self, field, value):
# Done
return self
-
def match_sources(self, sources):
"""Add sources to match to the query.
Arguments:
sources (str or list of str): The sources to match.
-
+
Returns:
self (Forge): For chaining.
"""
@@ -347,7 +374,6 @@ def match_sources(self, sources):
self.match_field(field="mdf.source_name", value=src, required=False, new_group=False)
return self
-
def match_ids(self, mdf_ids):
"""Match all the IDs in the given mdf_id list.
@@ -369,14 +395,13 @@ def match_ids(self, mdf_ids):
self.match_field(field="mdf.mdf_id", value=mid, required=False, new_group=False)
return self
-
def match_elements(self, elements, match_all=True):
"""Add elemental abbreviations to the query.
Arguments:
elements (str or list of str): The elements to match.
match_all (bool): If True, will add with AND. If False, will use OR. Default True.
-
+
Returns:
self (Forge): For chaining.
"""
@@ -393,7 +418,6 @@ def match_elements(self, elements, match_all=True):
new_group=False)
return self
-
def match_tags(self, tags, match_all=True):
"""Add tags to the query.
@@ -416,7 +440,6 @@ def match_tags(self, tags, match_all=True):
new_group=False)
return self
-
def match_titles(self, titles):
"""Add titles to the query.
@@ -436,6 +459,58 @@ def match_titles(self, titles):
self.match_field(field="mdf.title", value=title, required=False, new_group=False)
return self
+ def match_years(self, years=None, start=None, stop=None, inclusive=True):
+ """Add years and limits to the query.
+
+ Arguments:
+ years (int or string, or list of int or strings): The years to match.
+ Note that this argument overrides the start, stop, and inclusive arguments.
+ start (int or string): The lower range of years to match.
+ stop (int or string): The upper range of years to match.
+ inclusive (bool): If True, the start and stop values will be included in the search.
+ If False, they will be excluded.
+ Default True.
+ Returns:
+ self (Forge): For chaining.
+ """
+ # If nothing supplied, nothing to match
+ if years is None and start is None and stop is None:
+ return self
+
+ if years is not None and years != []:
+ if not isinstance(years, list):
+ years = [years]
+ years_int = []
+ for year in years:
+ try:
+ y_int = int(year)
+ years_int.append(y_int)
+ except ValueError:
+ print_("Invalid year: '", year, "'", sep="")
+
+ # Only match years if valid years were supplied
+ if len(years_int) > 0:
+ self.match_field(field="mdf.year", value=years_int[0], required=True,
+ new_group=True)
+ for year in years_int[1:]:
+ self.match_field(field="mdf.year", value=year, required=False, new_group=False)
+ else:
+ if start is not None:
+ try:
+ start = int(start)
+ except ValueError:
+ print_("Invalid start year: '", start, "'", sep="")
+ start = None
+ if stop is not None:
+ try:
+ stop = int(stop)
+ except ValueError:
+ print_("Invalid stop year: '", stop, "'", sep="")
+ stop = None
+
+ self.match_range(field="mdf.year", start=start, stop=stop,
+ inclusive=inclusive, required=True, new_group=True)
+ return self
def match_resource_types(self, types):
"""Match the given resource types.
@@ -457,13 +532,14 @@ def match_resource_types(self, types):
for rt in types[1:]:
self.match_field(field="mdf.resource_type", value=rt, required=False, new_group=False)
return self
-
-#################################################
-## Premade searches
-#################################################
- def search_by_elements(self, elements, sources=[], limit=None, match_all=True, info=False):
+# ***********************************************
+# * Premade searches
+# ***********************************************
+
+ def search_by_elements(self, elements, sources=[], index=None, limit=None,
+ match_all=True, info=False):
"""Execute a search for the given elements in the given sources.
search_by_elements([x], [y]) is equivalent to
match_elements([x]).match_sources([y]).search()
@@ -472,9 +548,10 @@ def search_by_elements(self, elements, sources=[], limit=None, match_all=True, i
Arguments:
elements (list of str): The elements to match. Default [].
sources (list of str): The sources to match. Default [].
+ index (str): The Globus Search index to search on. Defaults to the current index.
limit (int): The maximum number of results to return.
The max for this argument is the SEARCH_LIMIT imposed by Globus Search.
- match_all (bool): If True, will add elements with AND.
+ match_all (bool): If True, will add elements with AND.
If False, will use OR.
Default True.
info (bool): If False, search will return a list of the results.
@@ -488,15 +565,15 @@ def search_by_elements(self, elements, sources=[], limit=None, match_all=True, i
"""
return (self.match_elements(elements, match_all=match_all)
.match_sources(sources)
- .search(limit=limit, info=info))
-
+ .search(index=index, limit=limit, info=info))
- def search_by_titles(self, titles, limit=None, info=False):
+ def search_by_titles(self, titles, index=None, limit=None, info=False):
"""Execute a search for the given titles.
search_by_titles([x]) is equivalent to match_titles([x]).search()
Arguments:
titles (list of str): The titles to match. Default [].
+ index (str): The Globus Search index to search on. Defaults to the current index.
limit (int): The maximum number of results to return.
The max for this argument is the SEARCH_LIMIT imposed by Globus Search.
info (bool): If False, search will return a list of the results.
@@ -508,15 +585,15 @@ def search_by_titles(self, titles, limit=None, info=False):
list (if info=False): The results.
tuple (if info=True): The results, and a dictionary of query information.
"""
- return self.match_titles(titles).search(limit=limit, info=info)
+ return self.match_titles(titles).search(index=index, limit=limit, info=info)
-
- def search_by_tags(self, tags, limit=None, match_all=True, info=False):
+ def search_by_tags(self, tags, index=None, limit=None, match_all=True, info=False):
"""Execute a search for the given tag.
search_by_tags([x]) is equivalent to match_tags([x]).search()
Arguments:
tags (list of str): The tags to match. Default [].
+ index (str): The Globus Search index to search on. Defaults to the current index.
limit (int): The maximum number of results to return.
The max for this argument is the SEARCH_LIMIT imposed by Globus Search.
match_all (bool): If True, will add elements with AND.
@@ -531,22 +608,23 @@ def search_by_tags(self, tags, limit=None, match_all=True, info=False):
list (if info=False): The results.
tuple (if info=True): The results, and a dictionary of query information.
"""
- return self.match_tags(tags, match_all=match_all).search(limit=limit, info=info)
-
+ return self.match_tags(tags, match_all=match_all).search(index=index,
+ limit=limit,
+ info=info)
- def aggregate_source(self, sources):
+ def aggregate_source(self, sources, index=None):
"""Aggregate all records from a given source.
There is no limit to the number of results returned.
Please beware of aggregating very large datasets.
Arguments:
sources (str or list of str): The source to aggregate.
-
+ index (str): The Globus Search index to search on. Defaults to the current index.
+
Returns:
list of dict: All of the records from the source.
"""
- return self.match_sources(sources).aggregate()
-
+ return self.match_sources(sources).aggregate(index=index)
def fetch_datasets_from_results(self, entries=None, query=None, reset_query=True):
"""Retrieve the dataset entries for given records.
@@ -593,9 +671,9 @@ def fetch_datasets_from_results(self, entries=None, query=None, reset_query=True
return self.match_ids(list(ds_ids)).search()
-#################################################
-## Data retrieval functions
-#################################################
+# ***********************************************
+# * Data retrieval functions
+# ***********************************************
def http_download(self, results, dest=".", preserve_dir=False, verbose=True):
"""Download data files from the provided results using HTTPS.
@@ -614,7 +692,18 @@ def http_download(self, results, dest=".", preserve_dir=False, verbose=True):
verbose (bool): If True, status and progress messages will be printed.
If False, only error messages will be printed.
Default True.
+
+ Returns:
+ dict: success (bool): True if the operation succeeded.
+ False if it failed (implies message).
+ message (str): The error message. Not present when success is True.
"""
+ if self.__anonymous:
+ print_("Error: Anonymous HTTP download not yet supported.")
+ return {
+ "success": False,
+ "message": "Anonymous HTTP download not yet supported."
+ }
# If user submitted single result, make into list
if isinstance(results, dict):
results = [results]
@@ -633,7 +722,7 @@ def http_download(self, results, dest=".", preserve_dir=False, verbose=True):
+ str(HTTP_NUM_LIMIT)
+ " entries.")
}
- for res in tqdm(results, desc="Fetching files", disable= not verbose):
+ for res in tqdm(results, desc="Fetching files", disable=(not verbose)):
for key in res["mdf"]["links"].keys():
dl = res["mdf"]["links"][key]
host = dl.get("http_host", None) if type(dl) is dict else None
@@ -642,7 +731,7 @@ def http_download(self, results, dest=".", preserve_dir=False, verbose=True):
# local_path should be either dest + whole path or dest + filename
if preserve_dir:
local_path = os.path.normpath(dest + "/" + dl["path"])
- else:
+ else:
local_path = os.path.normpath(dest + "/" + os.path.basename(dl["path"]))
# Make dirs for storing the file if they don't exist
# preserve_dir doesn't matter; local_path has accounted for it already
@@ -683,14 +772,16 @@ def http_download(self, results, dest=".", preserve_dir=False, verbose=True):
self.response = requests.get(host+remote_path, headers=headers)
# Handle other errors by passing the buck to the user
if response.status_code != 200:
- print_("Error ", response.status_code,
+ print_("Error ", response.status_code,
" when attempting to access '",
host+remote_path, "'", sep="")
else:
# Write out the binary response content
with open(local_path, 'wb') as output:
output.write(response.content)
-
+ return {
+ "success": True
+ }
def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
wait_for_completion=True, verbose=True):
@@ -703,20 +794,26 @@ def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
dest (str): The destination path for the data files on the local machine.
Default current directory.
preserve_dir (bool): If True, the directory structure for the data files will be
- recreated at the destination. The path to the new files
+ recreated at the destination. The path to the new files
will be relative to the `dest` path
If False, only the data files themselves will be saved.
Default False.
wait_for_completion (bool): If True, will block until the transfer is finished.
If False, will not block.
Default True.
- verbose (bool): If True, status and progress messages will be print_ed.
- If False, only error messages will be print_ed.
+ verbose (bool): If True, status and progress messages will be printed.
+ If False, only error messages will be printed.
Default True.
Returns:
- list of str: task IDs of the Gloubs transfers
+ list of str: task IDs of the Globus transfers
"""
+ if self.__anonymous:
+ print_("Error: Anonymous Globus Transfer not supported.")
+ return {
+ "success": False,
+ "message": "Anonymous Globus Transfer not supported."
+ }
dest = os.path.abspath(dest)
# If results have info attached, remove it
if type(results) is tuple:
@@ -729,10 +826,9 @@ def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
# Assemble the transfer data
tasks = {}
filenames = set()
- for res in tqdm(results, desc="Processing records", disable= not verbose):
+ for res in tqdm(results, desc="Processing records", disable=(not verbose)):
found = False
- for key in tqdm(res["mdf"]["links"].keys(), desc="Fetching files", disable=True):
-
+ for key in res["mdf"]["links"].keys():
# Get the location of the data
dl = res["mdf"]["links"][key]
host = dl.get("globus_endpoint", None) if type(dl) is dict else None
@@ -744,12 +840,12 @@ def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
remote_path = dl["path"]
# local_path should be either dest + whole path or dest + filename
if preserve_dir:
- # remote_path is absolute, so os.path.join does not work
- local_path = os.path.abspath(dest + remote_path)
+ # remote_path is absolute, so os.path.join does not work
+ local_path = os.path.abspath(dest + remote_path)
else:
local_path = os.path.abspath(
- os.path.join(dest,
- os.path.basename(remote_path)))
+ os.path.join(dest,
+ os.path.basename(remote_path)))
# Make dirs for storing the file if they don't exist
# preserve_dir doesn't matter; local_path has accounted for it already
@@ -792,16 +888,16 @@ def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
# if need be, send it early
if host not in tasks.keys():
tasks[host] = globus_sdk.TransferData(self.__transfer_client,
- host, dest_ep, verify_checksum=True)
+ host, dest_ep,
+ verify_checksum=True)
tasks[host].add_item(remote_path, local_path)
filenames.add(local_path)
if not found:
print_("Error on record: No globus_endpoint provided.\nRecord: ", + str(res))
-
# Submit the jobs
submissions = []
- for td in tqdm(tasks.values(), desc="Submitting transfers", disable= not verbose):
+ for td in tqdm(tasks.values(), desc="Submitting transfers", disable=(not verbose)):
result = self.__transfer_client.submit_transfer(td)
if result["code"] != "Accepted":
raise globus_sdk.GlobusError("Error submitting transfer:", result["message"])
@@ -811,13 +907,10 @@ def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
polling_interval=10):
if verbose:
print_("Transferring...")
- for event in transfer_client.task_event_list(res["task_id"]):
+ for event in self.__transfer_client.task_event_list(result["task_id"]):
if event["is_error"]:
- transfer_client.cancel_task(res["task_id"])
- raise GlobusError("Error: " + event["description"])
- if config_data["timeout"] and intervals >= timeout_intervals:
- transfer_client.cancel_task(res["task_id"])
- raise GlobusError("Transfer timed out.")
+ self.__transfer_client.cancel_task(result["task_id"])
+ raise globus_sdk.GlobusError("Error: " + event["description"])
submissions.append(result["task_id"])
if verbose:
@@ -825,7 +918,6 @@ def globus_download(self, results, dest=".", dest_ep=None, preserve_dir=False,
print_("Task IDs:", "\n".join(submissions))
return submissions
-
def http_stream(self, results, verbose=True):
"""Yield data files from the provided results using HTTPS, through a generator.
For more than HTTP_NUM_LIMIT (defined above) files, you should use globus_download(),
@@ -834,13 +926,20 @@ def http_stream(self, results, verbose=True):
Arguments:
results (dict): The records from which files should be fetched.
This should be the return value of a search method.
- verbose (bool): If True, status and progress messages will be print_ed.
- If False, only error messages will be print_ed.
+ verbose (bool): If True, status and progress messages will be printed.
+ If False, only error messages will be printed.
Default True.
Yields:
str: Text of each data file.
"""
+ if self.__anonymous:
+ print_("Error: Anonymous HTTP download not yet supported.")
+ yield {
+ "success": False,
+ "message": "Anonymous HTTP download not yet supported."
+ }
+ return
# If results have info attached, remove it
if type(results) is tuple:
results = results[0]
@@ -882,36 +981,17 @@ def http_stream(self, results, verbose=True):
yield response.text
- def http_return(self, results, verbose=True):
- """Return data files from the provided results using HTTPS.
- For more than HTTP_NUM_LIMIT (defined above) files, you should use globus_download(),
- which uses Globus Transfer.
-
- Arguments:
- results (dict): The records from which files should be fetched.
- This should be the return value of a search method.
- verbose (bool): If True, status and progress messages will be print_ed.
- If False, only error messages will be print_ed.
- Default True.
-
- Returns:
- list of str: Text data of the data files.
- """
- return list(self.http_stream(results, verbose=verbose))
-
-
-
class Query:
"""The Query class is meant for internal Forge use. Users should not instantiate
a Query object directly, as Forge already manages a Query,
but advanced users may do so at their own risk.
- Using Query directly is an unsupported behavior
+ Using Query directly is an unsupported behavior
and may have unexpected results or unlisted changes in the future.
Queries may end up wrapped in parentheses, which has no direct effect on the search.
Adding terms must be chained with .and() or .or().
Terms will not have spaces in between otherwise, and it is desirable to be explicit about
- which terms are required.
+ which terms are required.
"""
def __init__(self, search_client, q=None, limit=None, advanced=False):
"""Initialize the Query instance.
@@ -920,7 +1000,7 @@ def __init__(self, search_client, q=None, limit=None, advanced=False):
search_client (SearchClient): The Globus Search client to use for searching.
q (str): The query string to start with. Default nothing.
limit (int): The maximum number of results to return. Default None.
- advanced (bool): If True, will submit query in "advanced" mode ro enable field matches.
+ advanced (bool): If True, will submit query in "advanced" mode to enable field matches.
If False, only basic fulltext term matches will be supported.
Default False.
"""
@@ -931,7 +1011,19 @@ def __init__(self, search_client, q=None, limit=None, advanced=False):
# initialized is True if something has been added to the query
# __init__(), term(), and field() can change this value to True
self.initialized = not self.query == "("
+ # Search index UUIDs, which are required instead of names
+ self.__index_uuids = {
+ "mdf": "d6cc98c3-ff53-4ee2-b22b-c6f945c0d30c",
+ "mdf-test": "c082b745-32ac-4ad2-9cde-92393f6e505c",
+ "dlhub": "847c9105-18a0-4ffb-8a71-03dd76dfcc9d",
+ "dlhub-test": "5c89e0a9-00e5-4171-b415-814fe4d0b8af"
+ }
+ def __translate_index(self, index):
+ """Translate a known Globus Search index into the index UUID.
+ The UUID is now the only way to access indices.
+ """
+ return self.__index_uuids.get(index.strip().lower(), index)
def __clean_query_string(self, q):
"""Clean up a query string.
@@ -951,9 +1043,8 @@ def __clean_query_string(self, q):
q += ")"
while q.count(")") > q.count("("):
q = "(" + q
-
- return q.strip()
+ return q.strip()
def clean_query(self):
"""Returns the current query, cleaned for user consumption,
@@ -963,7 +1054,6 @@ def clean_query(self):
"""
return self.__clean_query_string(self.query)
-
def term(self, term):
"""Add a term to the query.
@@ -977,7 +1067,6 @@ def term(self, term):
self.initialized = True
return self
-
def field(self, field, value):
"""Add a field:value term to the query.
Matches will have field == value.
@@ -986,7 +1075,7 @@ def field(self, field, value):
Arguments:
field (str): The field to look in for the value.
value (str): The value to match.
-
+
Returns:
self (Query): For chaining.
"""
@@ -998,7 +1087,6 @@ def field(self, field, value):
self.initialized = True
return self
-
def operator(self, op, close_group=False):
"""Add operator between terms.
There must be a term added before using this method.
@@ -1026,7 +1114,6 @@ def operator(self, op, close_group=False):
self.query += op
return self
-
def and_join(self, close_group=False):
"""Combine terms with AND.
There must be a term added before using this method.
@@ -1048,7 +1135,6 @@ def and_join(self, close_group=False):
self.operator("AND", close_group=close_group)
return self
-
def or_join(self, close_group=False):
"""Combine terms with OR.
There must be a term added before using this method.
@@ -1070,19 +1156,18 @@ def or_join(self, close_group=False):
self.operator("OR", close_group=close_group)
return self
-
def negate(self):
"""Negates the next term with NOT."""
self.operator("NOT")
return self
-
- def search(self, q=None, advanced=None, limit=SEARCH_LIMIT, info=False):
+ def search(self, q=None, index=None, advanced=None, limit=SEARCH_LIMIT, info=False):
"""Execute a search and return the results.
Arguments:
q (str): The query to execute. Defaults to the current query, if any.
There must be some query to execute.
+ index (str): The Globus Search index to search on. Required.
advanced (bool): If True, will submit query in "advanced" mode to enable field matches.
If False, only basic fulltext term matches will be supported.
Default False.
@@ -1104,6 +1189,11 @@ def search(self, q=None, advanced=None, limit=SEARCH_LIMIT, info=False):
if not q.strip("()"):
print_("Error: No query")
return ([], {"error": "No query"}) if info else []
+ if index is None:
+ print_("Error: No index specified")
+ return ([], {"error": "No index"}) if info else []
+ else:
+ uuid_index = self.__translate_index(index)
if advanced is None or self.advanced:
advanced = self.advanced
if limit is None:
@@ -1120,14 +1210,15 @@ def search(self, q=None, advanced=None, limit=SEARCH_LIMIT, info=False):
"limit": limit,
"offset": 0
}
- res = toolbox.gmeta_pop(self.__search_client.structured_search(qu), info=info)
+ res = toolbox.gmeta_pop(self.__search_client.post_search(uuid_index, qu), info=info)
# Add additional info
if info:
res[1]["query"] = qu
+ res[1]["index"] = index
+ res[1]["index_uuid"] = uuid_index
return res
-
- def aggregate(self, q=None, scroll_size=SEARCH_LIMIT):
+ def aggregate(self, q=None, index=None, scroll_size=SEARCH_LIMIT):
"""Gather all results that match a specific query
Note that all aggregate queries run in advanced mode.
@@ -1145,18 +1236,26 @@ def aggregate(self, q=None, scroll_size=SEARCH_LIMIT):
if not q.strip("()"):
print_("Error: No query")
return []
+ if index is None:
+ print_("Error: No index specified")
+ return []
q = self.__clean_query_string(q)
- #TODO: Remove record restriction (all entries require scroll_id)
+ # TODO: Remove record restriction (all entries require scroll_id)
q += " AND mdf.resource_type:record"
# Get the total number of records
- total = self.search(q, limit=0, advanced=True, info=True)[1]["total_query_matches"]
+ total = self.search(q, index=index, limit=0, advanced=True,
+ info=True)[1]["total_query_matches"]
+
+ # If aggregate is unnecessary, use Search automatically instead
+ if total <= SEARCH_LIMIT:
+ return self.search(q, index=index, limit=SEARCH_LIMIT, advanced=True)
# Scroll until all results are found
output = []
- #TODO: scroll_pos = 0 (when all entries have scroll_id)
+ # TODO: scroll_pos = 0 (when all entries have scroll_id)
scroll_pos = 1
with tqdm(total=total) as pbar:
while len(output) < total:
@@ -1168,16 +1267,9 @@ def aggregate(self, q=None, scroll_size=SEARCH_LIMIT):
# scroll width is much smaller than that maximum
scroll_width = scroll_size
while True:
- struct_query = {
- "q": "(" + q + ') AND mdf.scroll_id:>=%d AND mdf.scroll_id:<%d' % (
- scroll_pos, scroll_pos+scroll_width),
- "advanced": True,
- "limit": SEARCH_LIMIT,
- "offset": 0
- }
query = "(" + q + ') AND (mdf.scroll_id:>=%d AND mdf.scroll_id:<%d)' % (
scroll_pos, scroll_pos+scroll_width)
- results, info = self.search(query, advanced=True, info=True)
+ results, info = self.search(query, index=index, advanced=True, info=True)
# Check to make sure that all the matching records were returned
if info["total_query_matches"] <= len(results):
@@ -1196,12 +1288,17 @@ def aggregate(self, q=None, scroll_size=SEARCH_LIMIT):
return output
+ def mapping(self, index):
+ """Fetch the mapping for the specified index.
- def mapping(self):
- """Fetch the mapping for the current index.
+ Arguments:
+ index (str): The index to map.
Returns:
dict: The full mapping for the index.
"""
- return self.__search_client.mapping()["mappings"]
-
+ return (self.__search_client.get(
+ # TODO: Re-enable when Search handles index UUIDs
+ # "/unstable/index/{}/mapping".format(self.__translate_index(index)))
+ "/unstable/index/{}/mapping".format(index))
+ ["mappings"])
diff --git a/setup.cfg b/setup.cfg
index 87d6b83..585ec41 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,7 +1,10 @@
[bdist_wheel]
# Forge is compatible with Python 2 and 3
-universal=1
+universal = 1
[tool:pytest]
addopts = --ignore=setup.py --cov=mdf_forge
+[flake8]
+exclude = .git,*.egg*
+max-line-length = 100
diff --git a/setup.py b/setup.py
index e27c92e..fd1d7ca 100644
--- a/setup.py
+++ b/setup.py
@@ -2,13 +2,16 @@
setup(
name='mdf_forge',
- version='0.5.0',
+ version='0.5.1',
packages=['mdf_forge'],
description='Materials Data Facility python package',
- long_description="Forge is the Materials Data Facility Python package to interface and leverage the MDF Data Discovery service. Forge allows users to perform simple queries and facilitiates moving and synthesizing results.",
+ long_description=("Forge is the Materials Data Facility Python package"
+ " to interface and leverage the MDF Data Discovery service. "
+ "Forge allows users to perform simple queries and "
+ "facilitiates moving and synthesizing results."),
install_requires=[
- "mdf-toolbox>=0.1.0",
- "globus-sdk>=1.2.1",
+ "mdf-toolbox>=0.1.4",
+ "globus-sdk>=1.4.1",
"requests>=2.18.4",
"tqdm>=4.19.4",
"six>=1.10.0"
diff --git a/tests/test_forge.py b/tests/test_forge.py
index 3216607..c0cd15c 100644
--- a/tests/test_forge.py
+++ b/tests/test_forge.py
@@ -1,33 +1,29 @@
import os
-import time
import types
import pytest
import globus_sdk
+from globus_sdk.exc import SearchAPIError
from mdf_forge import forge
from mdf_toolbox import toolbox
# Manually logging in for Query testing
-query_search_client = toolbox.login(credentials={"app_name": "MDF_Forge",
- "services": ["search"], "index": "mdf"})["search"]
+query_search_client = toolbox.login(credentials={"app_name": "MDF_Forge",
+ "services": ["search"]})["search"]
-############################
-# Query tests
-############################
-
def test_query_init():
q1 = forge.Query(query_search_client)
assert q1.query == "("
- assert q1.limit == None
- assert q1.advanced == False
- assert q1.initialized == False
+ assert q1.limit is None
+ assert q1.advanced is False
+ assert q1.initialized is False
q2 = forge.Query(query_search_client, q="mdf.source_name:oqmd", limit=5, advanced=True)
assert q2.query == "mdf.source_name:oqmd"
assert q2.limit == 5
- assert q2.advanced == True
- assert q2.initialized == True
+ assert q2.advanced is True
+ assert q2.initialized is True
def test_query_term():
@@ -35,7 +31,7 @@ def test_query_term():
# Single match test
assert isinstance(q.term("term1"), forge.Query)
assert q.query == "(term1"
- assert q.initialized == True
+ assert q.initialized is True
# Multi-match test
q.and_join().term("term2")
assert q.query == "(term1 AND term2"
@@ -76,112 +72,136 @@ def test_query_field():
def test_query_operator(capsys):
- q1 = forge.Query(query_search_client)
- assert q1.query == "("
+ q = forge.Query(query_search_client)
+ assert q.query == "("
# Add bad operator
- assert q1.operator("FOO") == q1
+ assert q.operator("FOO") == q
out, err = capsys.readouterr()
- assert "Error: 'FOO' is not a valid operator"
- assert q1.query == "("
+ assert "Error: 'FOO' is not a valid operator" in out
+ assert q.query == "("
# Test operator cleaning
- q1.operator(" and ")
- assert q1.query == "( AND "
+ q.operator(" and ")
+ assert q.query == "( AND "
# Test close_group
- q1.operator("OR", close_group=True)
- assert q1.query == "( AND ) OR ("
+ q.operator("OR", close_group=True)
+ assert q.query == "( AND ) OR ("
def test_query_and_join(capsys):
- q1 = forge.Query(query_search_client)
+ q = forge.Query(query_search_client)
# Test not initialized
- assert q1.and_join() == q1
+ assert q.and_join() == q
out, err = capsys.readouterr()
assert ("Error: You must add a term before adding an operator. "
"The current query has not been changed.") in out
# Regular join
- q1.term("foo").and_join()
- assert q1.query == "(foo AND "
+ q.term("foo").and_join()
+ assert q.query == "(foo AND "
# close_group
- q1.term("bar").and_join(close_group=True)
- assert q1.query == "(foo AND bar) AND ("
+ q.term("bar").and_join(close_group=True)
+ assert q.query == "(foo AND bar) AND ("
def test_query_or_join(capsys):
- q1 = forge.Query(query_search_client)
+ q = forge.Query(query_search_client)
# Test not initialized
- assert q1.or_join() == q1
+ assert q.or_join() == q
out, err = capsys.readouterr()
assert ("Error: You must add a term before adding an operator. "
"The current query has not been changed.") in out
# Regular join
- q1.term("foo").or_join()
- assert q1.query == "(foo OR "
+ q.term("foo").or_join()
+ assert q.query == "(foo OR "
# close_group
- q1.term("bar").or_join(close_group=True)
- assert q1.query == "(foo OR bar) OR ("
+ q.term("bar").or_join(close_group=True)
+ assert q.query == "(foo OR bar) OR ("
def test_query_search(capsys):
# Error on no query
- q1 = forge.Query(query_search_client)
- assert q1.search() == []
+ q = forge.Query(query_search_client)
+ assert q.search(index="mdf") == []
out, err = capsys.readouterr()
assert "Error: No query" in out
- assert q1.search(info=True) == ([], {"error": "No query"})
+ assert q.search(info=True) == ([], {"error": "No query"})
+
+ # Error on no index
+ assert q.search(q="abc") == []
+ out, err = capsys.readouterr()
+ assert "Error: No index specified" in out
+ assert q.search(q="abc", info=True) == ([], {"error": "No index"})
# Return info if requested
- q2 = forge.Query(query_search_client)
- res2 = q2.search(q="Al", info=False)
+ res2 = q.search(q="Al", index="mdf", info=False)
assert isinstance(res2, list)
assert isinstance(res2[0], dict)
- q3 = forge.Query(query_search_client)
- res3 = q3.search(q="Al", info=True)
+ res3 = q.search(q="Al", index="mdf", info=True)
assert isinstance(res3, tuple)
assert isinstance(res3[0], list)
assert isinstance(res3[0][0], dict)
assert isinstance(res3[1], dict)
# Check limit
- q4 = forge.Query(query_search_client)
- res4 = q4.search("oqmd", limit=3)
+ res4 = q.search("oqmd", index="mdf", limit=3)
assert len(res4) == 3
# Check limit correction
- q5 = forge.Query(query_search_client)
- res5 = q5.search("nist_xps_db", limit=20000)
+ res5 = q.search("nist_xps_db", index="mdf", limit=20000)
assert len(res5) == 10000
+ # Test index translation
+ # mdf = d6cc98c3-ff53-4ee2-b22b-c6f945c0d30c
+ res6 = q.search(q="data", index="mdf", limit=1, info=True)
+ assert len(res6[0]) == 1
+ assert res6[1]["index"] == "mdf"
+ assert res6[1]["index_uuid"] == "d6cc98c3-ff53-4ee2-b22b-c6f945c0d30c"
+ res7 = q.search(q="data", index="d6cc98c3-ff53-4ee2-b22b-c6f945c0d30c", limit=1, info=True)
+ assert len(res7[0]) == 1
+ assert res7[1]["index"] == "d6cc98c3-ff53-4ee2-b22b-c6f945c0d30c"
+ assert res7[1]["index_uuid"] == "d6cc98c3-ff53-4ee2-b22b-c6f945c0d30c"
+ with pytest.raises(SearchAPIError):
+ q.search(q="data", index="invalid", limit=1, info=True)
+
def test_query_aggregate(capsys):
- q1 = forge.Query(query_search_client)
+ q = forge.Query(query_search_client)
# Error on no query
- assert q1.aggregate() == []
+ assert q.aggregate() == []
out, err = capsys.readouterr()
assert "Error: No query" in out
+ # Error on no index
+ assert q.aggregate(q="abc") == []
+ out, err = capsys.readouterr()
+ assert "Error: No index specified" in out
+
# Basic aggregation
- res1 = q1.aggregate("mdf.source_name:nist_xps_db")
+ res1 = q.aggregate("mdf.source_name:nist_xps_db", index="mdf")
assert len(res1) > 10000
assert isinstance(res1[0], dict)
# Multi-dataset aggregation
- q2 = forge.Query(query_search_client)
- res2 = q2.aggregate("(mdf.source_name:nist_xps_db OR mdf.source_name:nist_janaf)")
+ res2 = q.aggregate("(mdf.source_name:nist_xps_db OR mdf.source_name:nist_janaf)",
+ index="mdf")
assert len(res2) > 10000
assert len(res2) > len(res1)
+ # Unnecessary aggregation fallback to .search()
+ # Check success in Coveralls
+ assert len(q.aggregate("mdf.source_name:hopv")) < 10000
+
def test_query_chaining():
- q1 = forge.Query(query_search_client)
- q1.field("source_name", "cip")
- q1.and_join()
- q1.field("elements", "Al")
- res1 = q1.search(limit=10000)
+ q = forge.Query(query_search_client)
+ q.field("source_name", "cip")
+ q.and_join()
+ q.field("elements", "Al")
+ res1 = q.search(limit=10000, index="mdf")
res2 = (forge.Query(query_search_client)
.field("source_name", "cip")
.and_join()
.field("elements", "Al")
- .search(limit=10000))
+ .search(limit=10000, index="mdf"))
assert all([r in res2 for r in res1]) and all([r in res1 for r in res2])
@@ -211,17 +231,12 @@ def test_query_cleaning():
assert q10.clean_query() == "term OR term2"
-
-############################
-# Forge tests
-############################
-
# Test properties
def test_forge_properties():
- f1 = forge.Forge()
- assert type(f1.search_client) is toolbox.SearchClient
- assert type(f1.transfer_client) is globus_sdk.TransferClient
- assert type(f1.mdf_authorizer) is globus_sdk.RefreshTokenAuthorizer
+ f = forge.Forge(index="mdf")
+ assert type(f.search_client) is globus_sdk.SearchClient
+ assert type(f.transfer_client) is globus_sdk.TransferClient
+ assert type(f.mdf_authorizer) is globus_sdk.RefreshTokenAuthorizer
# Sample results for download testing
@@ -288,12 +303,13 @@ def check_field(res, field, value):
"mdf.mdf_id",
"mdf.resource_type",
"mdf.title",
- "mdf.tags"
+ "mdf.tags",
+ "mdf.year"
]
if field not in supported_fields:
raise ValueError("Implement or re-spell "
+ field
- + "because check_field only works on "
+ + "because check_field only works on "
+ str(supported_fields))
# If no results, set matches to false
all_match = (len(res) > 0)
@@ -319,6 +335,8 @@ def check_field(res, field, value):
vals = r["mdf"]["tags"]
except KeyError:
vals = []
+ elif field == "mdf.year":
+ vals = [r["mdf"]["year"]]
# If a result does not contain the value, no match
if value not in vals:
@@ -332,93 +350,99 @@ def check_field(res, field, value):
some_match = True
if only_match:
- #print("Exclusive match")
+ # Exclusive match
return 0
elif all_match:
- #print("Inclusive match")
+ # Inclusive match
return 1
elif some_match:
- #print("Partial match")
+ # Partial match
return 2
else:
- #print("No match")
+ # No match
return -1
def test_forge_match_field():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
# Basic usage
- f1.match_field("mdf.source_name", "nist_janaf")
- res1 = f1.search()
+ f.match_field("mdf.source_name", "nist_janaf")
+ res1 = f.search()
assert check_field(res1, "mdf.source_name", "nist_janaf") == 0
# Check that query clears
- assert f1.search() == []
+ assert f.search() == []
+
# Also checking check_field
- f2 = forge.Forge()
- f2.match_field("mdf.elements", "Al")
- res2 = f2.search()
+ f.match_field("mdf.elements", "Al")
+ res2 = f.search()
assert check_field(res2, "mdf.elements", "Al") == 1
def test_forge_exclude_field():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
# Basic usage
- f1.exclude_field("mdf.elements", "Al")
- f1.match_field("mdf.source_name", "core_mof")
- res1 = f1.search()
+ f.exclude_field("mdf.elements", "Al")
+ f.match_field("mdf.source_name", "core_mof")
+ res1 = f.search()
assert check_field(res1, "mdf.elements", "Al") == -1
def test_forge_match_range():
# Single-value use
- f1 = forge.Forge()
- f1.match_range("mdf.elements", "Al", "Al")
- res1, info1 = f1.search(info=True)
+ f = forge.Forge(index="mdf")
+ f.match_range("mdf.elements", "Al", "Al")
+ res1, info1 = f.search(info=True)
assert check_field(res1, "mdf.elements", "Al") == 1
- f2 = forge.Forge()
- res2, info2 = f2.search("mdf.elements:Al", advanced=True, info=True)
+
+ res2, info2 = f.search("mdf.elements:Al", advanced=True, info=True)
assert info1["total_query_matches"] == info2["total_query_matches"]
+
# Non-matching use, test inclusive
- f3 = forge.Forge()
- f3.match_range("mdf.elements", "Al", "Al", inclusive=False)
- assert f3.search() == []
+ f.match_range("mdf.elements", "Al", "Al", inclusive=False)
+ assert f.search() == []
+
# Actual range
- f4 = forge.Forge()
- f4.match_range("mdf.elements", "Al", "Cu")
- res4, info4 = f4.search(info=True)
+ f.match_range("mdf.elements", "Al", "Cu")
+ res4, info4 = f.search(info=True)
assert info1["total_query_matches"] < info4["total_query_matches"]
assert (check_field(res4, "mdf.elements", "Al") >= 0 or
check_field(res4, "mdf.elements", "Cu") >= 0)
+ # Nothing to match
+ assert f.match_range("field", start=None, stop=None) == f
+
def test_forge_exclude_range():
# Single-value use
- f1 = forge.Forge()
- f1.exclude_range("mdf.elements", "Am", "*")
- f1.exclude_range("mdf.elements", "*", "Ak")
- res1, info1 = f1.search(info=True)
+ f = forge.Forge(index="mdf")
+ f.exclude_range("mdf.elements", "Am", "*")
+ f.exclude_range("mdf.elements", "*", "Ak")
+ res1, info1 = f.search(info=True)
assert (check_field(res1, "mdf.elements", "Al") == 0 or
check_field(res1, "mdf.elements", "Al") == 2)
- f2 = forge.Forge()
- res2, info2 = f2.search("mdf.elements:Al", advanced=True, info=True)
+
+ res2, info2 = f.search("mdf.elements:Al", advanced=True, info=True)
assert info1["total_query_matches"] <= info2["total_query_matches"]
+
# Non-matching use, test inclusive
- f3 = forge.Forge()
- f3.exclude_range("mdf.elements", "Am", "*")
- f3.exclude_range("mdf.elements", "*", "Ak")
- f3.exclude_range("mdf.elements", "Al", "Al", inclusive=False)
- res3, info3 = f3.search(info=True)
+ f.exclude_range("mdf.elements", "Am", "*")
+ f.exclude_range("mdf.elements", "*", "Ak")
+ f.exclude_range("mdf.elements", "Al", "Al", inclusive=False)
+ res3, info3 = f.search(info=True)
assert info1["total_query_matches"] == info3["total_query_matches"]
+ # Nothing to match
+ assert f.exclude_range("field", start=None, stop=None) == f
+
def test_forge_exclusive_match():
- f1 = forge.Forge()
- f1.exclusive_match("mdf.elements", "Al")
- res1 = f1.search()
+ f = forge.Forge(index="mdf")
+ f.exclusive_match("mdf.elements", "Al")
+ res1 = f.search()
assert check_field(res1, "mdf.elements", "Al") == 0
- f2 = forge.Forge()
- f2.exclusive_match("mdf.elements", ["Al", "Cu"])
- res2 = f2.search()
+
+ f.exclusive_match("mdf.elements", ["Al", "Cu"])
+ res2 = f.search()
assert check_field(res2, "mdf.elements", "Al") == 1
assert check_field(res2, "mdf.elements", "Cu") == 1
assert check_field(res2, "mdf.elements", "Cp") == -1
@@ -426,110 +450,106 @@ def test_forge_exclusive_match():
def test_forge_match_sources():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
# One source
- f1.match_sources("nist_janaf")
- res1 = f1.search()
+ f.match_sources("nist_janaf")
+ res1 = f.search()
assert res1 != []
assert check_field(res1, "mdf.source_name", "nist_janaf") == 0
+
# Multi-source
- f2 = forge.Forge()
- f2.match_sources(["nist_janaf", "hopv"])
- res2 = f2.search()
+ f.match_sources(["nist_janaf", "hopv"])
+ res2 = f.search()
# res1 is a subset of res2
assert len(res2) > len(res1)
assert all([r1 in res2 for r1 in res1])
assert check_field(res2, "mdf.source_name", "nist_janaf") == 2
+
# No source
- f3 = forge.Forge()
- assert f3.match_sources("") == f3
+ assert f.match_sources("") == f
def test_forge_match_ids():
# Get a couple IDs
- f0 = forge.Forge()
- res0 = f0.search("mdf.source_name:nist_janaf", advanced=True, limit=2)
+ f = forge.Forge(index="mdf")
+ res0 = f.search("mdf.source_name:nist_janaf", advanced=True, limit=2)
id1 = res0[0]["mdf"]["mdf_id"]
id2 = res0[1]["mdf"]["mdf_id"]
- f1 = forge.Forge()
+
# One ID
- f1.match_ids(id1)
- res1 = f1.search()
+ f.match_ids(id1)
+ res1 = f.search()
assert res1 != []
assert check_field(res1, "mdf.mdf_id", id1) == 0
+
# Multi-ID
- f2 = forge.Forge()
- f2.match_ids([id1, id2])
- res2 = f2.search()
+ f.match_ids([id1, id2])
+ res2 = f.search()
# res1 is a subset of res2
assert len(res2) > len(res1)
assert all([r1 in res2 for r1 in res1])
assert check_field(res2, "mdf.mdf_id", id2) == 2
+
# No id
- f3 = forge.Forge()
- assert f3.match_ids("") == f3
+ assert f.match_ids("") == f
def test_forge_match_elements():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
# One element
- f1.match_elements("Al")
- res1 = f1.search()
+ f.match_elements("Al")
+ res1 = f.search()
assert res1 != []
check_val1 = check_field(res1, "mdf.elements", "Al")
assert check_val1 == 0 or check_val1 == 1
+
# Multi-element
- f2 = forge.Forge()
- f2.match_elements(["Al", "Cu"])
- res2 = f2.search()
+ f.match_elements(["Al", "Cu"])
+ res2 = f.search()
assert check_field(res2, "mdf.elements", "Al") == 1
assert check_field(res2, "mdf.elements", "Cu") == 1
+
# No elements
- f3 = forge.Forge()
- assert f3.match_elements("") == f3
+ assert f.match_elements("") == f
def test_forge_match_titles():
# One title
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
titles1 = '"OQMD - Na1Y2Zr1"'
- res1 = f1.match_titles(titles1).search()
+ res1 = f.match_titles(titles1).search()
assert res1 != []
assert check_field(res1, "mdf.title", "OQMD - Na1Y2Zr1") == 0
# Multiple titles
- f2 = forge.Forge()
titles2 = ['"AMCS - Tungsten"', '"Cytochrome QSAR"']
- res2 = f2.match_titles(titles2).search()
+ res2 = f.match_titles(titles2).search()
assert res2 != []
assert check_field(res2, "mdf.title", "Cytochrome QSAR - C13F2N6O") == 2
# No titles
- f3 = forge.Forge()
- assert f3.match_titles("") == f3
+ assert f.match_titles("") == f
def test_forge_match_tags():
# Get one tag
- f0 = forge.Forge()
- res0 = f0.search("mdf.source_name:trinkle_elastic_fe_bcc", advanced=True, limit=1)
+ f = forge.Forge(index="mdf")
+ res0 = f.search("mdf.source_name:trinkle_elastic_fe_bcc", advanced=True, limit=1)
tags1 = res0[0]["mdf"]["tags"][0]
+
# One tag
- f1 = forge.Forge()
- res1 = f1.match_tags(tags1).search()
+ res1 = f.match_tags(tags1).search()
assert check_field(res1, "mdf.tags", tags1) == 2
- f2 = forge.Forge()
tags2 = "\"ab initio\""
- f2.match_tags(tags2)
- res2 = f2.search()
+ f.match_tags(tags2)
+ res2 = f.search()
# Elasticsearch splits ["ab-initio"] into ["ab", "initio"]
assert check_field(res2, "mdf.tags", "ab-initio") == 2
# Multiple tags
- f3 = forge.Forge()
tags3 = ["\"density functional theory calculations\"", "\"X-ray\""]
- res3 = f3.match_tags(tags3, match_all=True).search()
+ res3 = f.match_tags(tags3, match_all=True).search()
# "source_name": "ge_nanoparticles",
# "tags": [ "amorphization","density functional theory calculations","Ge nanoparticles",
# "high pressure","phase transformation","Raman","X-ray absorption","zip" ]
@@ -538,96 +558,133 @@ def test_forge_match_tags():
assert check_field(res3, "mdf.tags", "density functional theory calculations") == 1
# No tag
- f4 = forge.Forge()
- assert f4.match_tags("") == f4
+ assert f.match_tags("") == f
+
+
+def test_forge_match_years(capsys):
+ # One year of data/results
+ f = forge.Forge(index="mdf")
+ res1 = f.match_years("2015").search()
+ assert res1 != []
+ assert check_field(res1, "mdf.year", 2015) == 0
+
+ # Multiple years
+ res2 = f.match_years(years=["2015", 2011]).search()
+ assert check_field(res2, "mdf.year", 2011) == 2
+
+ # Wrong input
+ f.match_years(["20x5"]).search()
+ out, err = capsys.readouterr()
+ assert "Invalid year: '20x5'" in out
+
+ f.match_years(start="20x5").search()
+ out, err = capsys.readouterr()
+ assert "Invalid start year: '20x5'" in out
+
+ f.match_years(stop="20x5").search()
+ out, err = capsys.readouterr()
+ assert "Invalid stop year: '20x5'" in out
+
+ assert f.match_years() == f
+
+ # Test range
+ res4 = f.match_years(start=2015, stop=2015, inclusive=True).search()
+ assert check_field(res4, "mdf.year", 2015) == 0
+
+ res5 = f.match_years(start=2014, stop=2017, inclusive=False).search()
+ assert check_field(res5, "mdf.year", 2013) == -1
+ assert check_field(res5, "mdf.year", 2014) == -1
+ assert check_field(res5, "mdf.year", 2015) == 2
+ assert check_field(res5, "mdf.year", 2016) == 2
+ assert check_field(res5, "mdf.year", 2017) == -1
+
+ assert f.match_years(start=2015, stop=2015, inclusive=False).search() == []
def test_forge_match_resource_types():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
# Test one type
- f1.match_resource_types("record")
- res1 = f1.search(limit=10)
+ f.match_resource_types("record")
+ res1 = f.search(limit=10)
assert check_field(res1, "mdf.resource_type", "record") == 0
+
# Test two types
- f2 = forge.Forge()
- f2.match_resource_types(["collection", "dataset"])
- res2 = f2.search()
+ f.match_resource_types(["collection", "dataset"])
+ res2 = f.search()
assert check_field(res2, "mdf.resource_type", "record") == -1
- #TODO: Re-enable this assert after we get collections in MDF
+ # TODO: Re-enable this assert after we get collections in MDF
# assert check_field(res2, "mdf.resource_type", "dataset") == 2
+
# Test zero types
- f3 = forge.Forge()
- assert f3.match_resource_types("") == f3
+ assert f.match_resource_types("") == f
def test_forge_search(capsys):
# Error on no query
- f1 = forge.Forge()
- assert f1.search() == []
+ f = forge.Forge(index="mdf")
+ assert f.search() == []
out, err = capsys.readouterr()
assert "Error: No query" in out
# Return info if requested
- f2 = forge.Forge()
- res2 = f2.search(q="Al", info=False)
+ res2 = f.search(q="Al", info=False, index="mdf")
assert isinstance(res2, list)
assert isinstance(res2[0], dict)
- f3 = forge.Forge()
- res3 = f3.search(q="Al", info=True)
+
+ res3 = f.search(q="Al", info=True)
assert isinstance(res3, tuple)
assert isinstance(res3[0], list)
assert isinstance(res3[0][0], dict)
assert isinstance(res3[1], dict)
# Check limit
- f4 = forge.Forge()
- res4 = f4.search("oqmd", limit=3)
+ res4 = f.search("oqmd", limit=3)
assert len(res4) == 3
# Check reset_query
- f5 = forge.Forge()
- f5.match_field("mdf.source_name", "hopv")
- res5 = f5.search(reset_query=False)
- res6 = f5.search()
+ f.match_field("mdf.source_name", "hopv")
+ res5 = f.search(reset_query=False)
+ res6 = f.search()
assert all([r in res6 for r in res5]) and all([r in res5 for r in res6])
+ # Check default index
+ f2 = forge.Forge()
+ assert f2.search("data", limit=1, info=True)[1]["index"] == "mdf"
+
def test_forge_search_by_elements():
- f1 = forge.Forge()
- f2 = forge.Forge()
+ f = forge.Forge(index="mdf")
elements = ["Cu", "Al"]
sources = ["oqmd", "nist_xps_db"]
- res1, info1 = f1.match_sources(sources).match_elements(elements).search(limit=10000, info=True)
- res2, info2 = f2.search_by_elements(elements, sources, limit=10000, info=True)
+ res1, info1 = f.match_sources(sources).match_elements(elements).search(limit=10000, info=True)
+ res2, info2 = f.search_by_elements(elements, sources, limit=10000, info=True)
assert all([r in res2 for r in res1]) and all([r in res1 for r in res2])
assert check_field(res1, "mdf.elements", "Al") == 1
assert check_field(res1, "mdf.source_name", "oqmd") == 2
def test_forge_search_by_titles():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
titles1 = ["\"AMCS - Tungsten\""]
- res1 = f1.search_by_titles(titles1)
+ res1 = f.search_by_titles(titles1)
assert check_field(res1, "mdf.title", "AMCS - Tungsten") == 0
- f2 = forge.Forge()
titles2 = ["Tungsten"]
- res2 = f2.search_by_titles(titles2)
+ res2 = f.search_by_titles(titles2)
assert check_field(res2, "mdf.title", "AMCS - Tungsten") == 2
def test_forge_search_by_tags():
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
tags1 = "DFT"
- res1 = f1.search_by_tags(tags1)
+ res1 = f.search_by_tags(tags1)
assert check_field(res1, "mdf.tags", "DFT") == 2
- f2 = forge.Forge()
tags2 = ["\"Density Functional Theory\"", "\"X-ray\""]
- res2 = f2.search_by_tags(tags2, match_all=True)
- f3 = forge.Forge()
+ res2 = f.search_by_tags(tags2, match_all=True)
+
tags3 = ["\"Density Functional Theory\"", "\"X-ray\""]
- res3 = f3.search_by_tags(tags3, match_all=False)
+ res3 = f.search_by_tags(tags3, match_all=False)
# res2 is a subset of res3
assert len(res3) > len(res2)
@@ -636,8 +693,8 @@ def test_forge_search_by_tags():
def test_forge_aggregate_source():
# Test limit
- f1 = forge.Forge()
- res1 = f1.aggregate_source("amcs")
+ f = forge.Forge(index="mdf")
+ res1 = f.aggregate_source("amcs")
assert isinstance(res1, list)
assert len(res1) > 10000
assert isinstance(res1[0], dict)
@@ -645,68 +702,67 @@ def test_forge_aggregate_source():
def test_forge_fetch_datasets_from_results():
# Get some results
+ f = forge.Forge(index="mdf")
# Record from OQMD
- res01 = forge.Forge().search("mdf.source_name:oqmd AND mdf.resource_type:record",
- advanced=True, limit=1)
+ res01 = f.search("mdf.source_name:oqmd AND mdf.resource_type:record", advanced=True, limit=1)
# Record from OQMD with info
- res02 = forge.Forge().search("mdf.source_name:oqmd AND mdf.resource_type:record",
- advanced=True, limit=1, info=True)
+ res02 = f.search("mdf.source_name:oqmd AND mdf.resource_type:record",
+ advanced=True, limit=1, info=True)
# Records from JANAF
- res03 = forge.Forge().search("mdf.source_name:nist_janaf AND mdf.resource_type:record",
- advanced=True, limit=2)
+ res03 = f.search("mdf.source_name:nist_janaf AND mdf.resource_type:record",
+ advanced=True, limit=2)
# Dataset for NIST XPS DB
- res04 = forge.Forge().search("mdf.source_name:nist_xps_db AND mdf.resource_type:dataset",
- advanced=True)
+ res04 = f.search("mdf.source_name:nist_xps_db AND mdf.resource_type:dataset", advanced=True)
# Get the correct dataset entries
- oqmd = forge.Forge().search("mdf.source_name:oqmd AND mdf.resource_type:dataset",
- advanced=True)[0]
- nist_janaf = forge.Forge().search("mdf.source_name:nist_janaf AND mdf.resource_type:dataset",
- advanced=True)[0]
+ oqmd = f.search("mdf.source_name:oqmd AND mdf.resource_type:dataset", advanced=True)[0]
+ nist_janaf = f.search("mdf.source_name:nist_janaf AND mdf.resource_type:dataset",
+ advanced=True)[0]
# Fetch single dataset
- f1 = forge.Forge()
- res1 = f1.fetch_datasets_from_results(res01[0])
+ res1 = f.fetch_datasets_from_results(res01[0])
assert res1[0] == oqmd
+
# Fetch dataset with results + info
- f2 = forge.Forge()
- res2 = f2.fetch_datasets_from_results(res02)
+ res2 = f.fetch_datasets_from_results(res02)
assert res2[0] == oqmd
+
# Fetch multiple datasets
- f3 = forge.Forge()
rtemp = res01+res03
- res3 = f3.fetch_datasets_from_results(rtemp)
+ res3 = f.fetch_datasets_from_results(rtemp)
assert len(res3) == 2
assert oqmd in res3
assert nist_janaf in res3
+
# Fetch dataset from dataset
- f4 = forge.Forge()
- res4 = f4.fetch_datasets_from_results(res04)
+ res4 = f.fetch_datasets_from_results(res04)
assert res4 == res04
+
# Fetch entries from current query
- f5 = forge.Forge()
- f5.match_sources("nist_xps_db")
- assert f5.fetch_datasets_from_results() == res04
+ f.match_sources("nist_xps_db")
+ assert f.fetch_datasets_from_results() == res04
+
# Fetch nothing
- f6 = forge.Forge()
unknown_entry = {"mdf": {"resource_type": "unknown"}}
- assert f6.fetch_datasets_from_results(unknown_entry) == []
+ assert f.fetch_datasets_from_results(unknown_entry) == []
def test_forge_aggregate():
# Test that aggregate uses the current query properly
# And returns results
# And respects the reset_query arg
- f1 = forge.Forge()
- f1.match_field("mdf.source_name", "nist_xps_db")
- res1 = f1.aggregate(reset_query=False)
+ f = forge.Forge(index="mdf")
+ f.match_field("mdf.source_name", "nist_xps_db")
+ res1 = f.aggregate(reset_query=False, index="mdf")
assert len(res1) > 10000
- res2 = f1.aggregate()
- assert all([r in res2 for r in res1]) and all([r in res1 for r in res2])
+ assert check_field(res1, "mdf.source_name", "nist_xps_db") == 0
+ res2 = f.aggregate()
+ assert len(res2) == len(res1)
+ assert check_field(res2, "mdf.source_name", "nist_xps_db") == 0
def test_forge_reset_query():
- f = forge.Forge()
+ f = forge.Forge(index="mdf")
# Term will return results
f.match_field("elements", "Al")
f.reset_query()
@@ -715,14 +771,14 @@ def test_forge_reset_query():
def test_forge_current_query():
- f = forge.Forge()
+ f = forge.Forge(index="mdf")
# Query.clean_query() is already tested, just need to check basic functionality
f.match_field("field", "value")
assert f.current_query() == "(field:value)"
def test_forge_http_download(capsys):
- f = forge.Forge()
+ f = forge.Forge(index="mdf")
# Simple case
f.http_download(example_result1)
assert os.path.exists("./test_fetch.txt")
@@ -751,7 +807,7 @@ def test_forge_http_download(capsys):
os.remove(os.path.join(dest_path, "test_multifetch.txt"))
# Too many files
- assert f.http_download(list(range(10001)))["success"] == False
+ assert f.http_download(list(range(10001)))["success"] is False
# "Missing" files
f.http_download(example_result_missing)
@@ -763,17 +819,19 @@ def test_forge_http_download(capsys):
@pytest.mark.xfail(reason="Test relies on get_local_ep() which can require user input.")
def test_forge_globus_download():
- f = forge.Forge()
+ f = forge.Forge(index="mdf")
# Simple case
- res1 = f.globus_download(example_result1)
+ f.globus_download(example_result1)
assert os.path.exists("./test_fetch.txt")
os.remove("./test_fetch.txt")
+
# With dest and preserve_dir
dest_path = os.path.expanduser("~/mdf")
f.globus_download(example_result1, dest=dest_path, preserve_dir=True)
assert os.path.exists(os.path.join(dest_path, "test", "test_fetch.txt"))
os.remove(os.path.join(dest_path, "test", "test_fetch.txt"))
os.rmdir(os.path.join(dest_path, "test"))
+
# With multiple files
f.globus_download(example_result2, dest=dest_path)
assert os.path.exists(os.path.join(dest_path, "test_fetch.txt"))
@@ -783,19 +841,21 @@ def test_forge_globus_download():
def test_forge_http_stream(capsys):
- f1 = forge.Forge()
+ f = forge.Forge(index="mdf")
# Simple case
- res1 = f1.http_stream(example_result1)
+ res1 = f.http_stream(example_result1)
assert isinstance(res1, types.GeneratorType)
assert next(res1) == "This is a test document for Forge testing. Please do not remove.\n"
+
# With multiple files
- res2 = f1.http_stream((example_result2, {"info":{}}))
+ res2 = f.http_stream((example_result2, {"info": {}}))
assert isinstance(res2, types.GeneratorType)
assert next(res2) == "This is a test document for Forge testing. Please do not remove.\n"
assert next(res2) == "This is a second test document for Forge testing. Please do not remove.\n"
+
# Too many results
- res3 = f1.http_stream(list(range(10001)))
- assert next(res3)["success"] == False
+ res3 = f.http_stream(list(range(10001)))
+ assert next(res3)["success"] is False
out, err = capsys.readouterr()
assert ("Too many results supplied. Use globus_download() for "
"fetching more than 2000 entries.") in out
@@ -803,40 +863,51 @@ def test_forge_http_stream(capsys):
next(res3)
# "Missing" files
- f2 = forge.Forge()
- assert next(f2.http_stream(example_result_missing)) is None
+ assert next(f.http_stream(example_result_missing)) is None
out, err = capsys.readouterr()
assert not os.path.exists("./missing.txt")
assert ("Error 404 when attempting to access "
"'https://data.materialsdatafacility.org/test/missing.txt'") in out
-def test_forge_http_return():
- f = forge.Forge()
- # Simple case
- res1 = f.http_return(example_result1)
- assert isinstance(res1, list)
- assert res1 == ["This is a test document for Forge testing. Please do not remove.\n"]
- # With multiple files
- res2 = f.http_return(example_result2)
- assert isinstance(res2, list)
- assert res2 == ["This is a test document for Forge testing. Please do not remove.\n",
- "This is a second test document for Forge testing. Please do not remove.\n"]
-
-
def test_forge_chaining():
- f1 = forge.Forge()
- f1.match_field("source_name", "cip")
- f1.match_field("elements", "Al")
- res1 = f1.search()
- res2 = forge.Forge().match_field("source_name", "cip").match_field("elements", "Al").search()
+ f = forge.Forge(index="mdf")
+ f.match_field("source_name", "cip")
+ f.match_field("elements", "Al")
+ res1 = f.search()
+ res2 = f.match_field("source_name", "cip").match_field("elements", "Al").search()
assert all([r in res2 for r in res1]) and all([r in res1 for r in res2])
def test_forge_show_fields():
- f1 = forge.Forge()
- res1 = f1.show_fields()
+ f = forge.Forge(index="mdf")
+ res1 = f.show_fields()
assert "mdf" in res1.keys()
- res2 = f1.show_fields("mdf")
+ res2 = f.show_fields(block="mdf", index="mdf")
assert "mdf.mdf_id" in res2.keys()
+
+def test_forge_anonymous(capsys):
+ f = forge.Forge(anonymous=True)
+ # Test search
+ assert len(f.search("mdf.source_name:oqmd", advanced=True, limit=300)) == 300
+
+ # Test aggregation
+ assert len(f.aggregate("mdf.source_name:nist_xps_db")) > 10000
+
+ # Error on auth-only functions
+ # http_download
+ assert f.http_download({})["success"] is False
+ out, err = capsys.readouterr()
+ assert "Error: Anonymous HTTP download not yet supported." in out
+ # globus_download
+ assert f.globus_download({})["success"] is False
+ out, err = capsys.readouterr()
+ assert "Error: Anonymous Globus Transfer not supported." in out
+ # http_stream
+ res = f.http_stream({})
+ assert next(res)["success"] is False
+ out, err = capsys.readouterr()
+ assert "Error: Anonymous HTTP download not yet supported." in out
+ with pytest.raises(StopIteration):
+ next(res)
diff --git a/travis.tar.enc b/travis.tar.enc
index 9c1007b..20ae823 100644
Binary files a/travis.tar.enc and b/travis.tar.enc differ