diff --git a/demos/demos_databases_apis/tigergraph/fraud.ipynb b/demos/demos_databases_apis/tigergraph/fraud_raw_REST_calls.ipynb
similarity index 88%
rename from demos/demos_databases_apis/tigergraph/fraud.ipynb
rename to demos/demos_databases_apis/tigergraph/fraud_raw_REST_calls.ipynb
index 75cf29222a..4a4ab9b39e 100644
--- a/demos/demos_databases_apis/tigergraph/fraud.ipynb
+++ b/demos/demos_databases_apis/tigergraph/fraud_raw_REST_calls.ipynb
@@ -4,9 +4,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Tigergraph<>Graphistry Fraud Demo\n",
+ "# Tigergraph<>Graphistry Fraud Demo: Raw REST\n",
"\n",
- "Accesses Tigergraph's fraud demo"
+ "Accesses Tigergraph's fraud demo directly via manual REST calls"
]
},
{
@@ -18,9 +18,6 @@
"height": 34
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "LDqOCGCFv5hV",
"outputId": "ce308f8c-c843-43b1-ee99-539cb78ceeaf"
},
@@ -44,8 +41,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "fX4NU9ZGH6Ln"
},
"outputs": [],
@@ -104,8 +99,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "LUEA1fmFOjCD"
},
"source": [
@@ -116,8 +109,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "rY8Ip6WcOnPl"
},
"source": [
@@ -130,9 +121,6 @@
"metadata": {
"colab": {},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "grR4ZNOAIIJn"
},
"outputs": [],
@@ -148,8 +136,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "UUTIiNkmIMQc"
},
"outputs": [],
@@ -161,8 +147,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "mXT2bD2UOp3o"
},
"source": [
@@ -175,9 +159,6 @@
"metadata": {
"colab": {},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "6fP3qhDCOwi3"
},
"outputs": [],
@@ -193,8 +174,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "6Z6ER8VGVi9t"
},
"outputs": [],
@@ -206,8 +185,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "SKepDGbKZLGI"
},
"source": [
@@ -221,8 +198,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "SNvvtNvGZM92"
},
"outputs": [],
@@ -237,8 +212,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "JD8M0c7OlYh7"
},
"source": [
@@ -252,8 +225,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "kdFC1-wglZIT"
},
"outputs": [],
@@ -273,8 +244,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "TczzpZRulnb4"
},
"outputs": [],
@@ -289,8 +258,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "0uOagyc1lqRR"
},
"outputs": [],
@@ -316,8 +283,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "Gc_Ot3E2lwOr"
},
"outputs": [],
@@ -341,16 +306,16 @@
"language_info": {
"codemirror_mode": {
"name": "ipython",
- "version": 2
+ "version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
- "pygments_lexer": "ipython2",
- "version": "2.7.13"
+ "pygments_lexer": "ipython3",
+ "version": "3.7.3"
}
},
"nbformat": 4,
- "nbformat_minor": 0
+ "nbformat_minor": 1
}
diff --git a/demos/demos_databases_apis/tigergraph/social.ipynb b/demos/demos_databases_apis/tigergraph/social_raw_REST_calls.ipynb
similarity index 89%
rename from demos/demos_databases_apis/tigergraph/social.ipynb
rename to demos/demos_databases_apis/tigergraph/social_raw_REST_calls.ipynb
index febce51d25..4b499e583f 100644
--- a/demos/demos_databases_apis/tigergraph/social.ipynb
+++ b/demos/demos_databases_apis/tigergraph/social_raw_REST_calls.ipynb
@@ -4,12 +4,10 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "ufnpFtXZvhXl"
},
"source": [
- "# Graphistry Tutorial: Notebooks + TigerGraph\n",
+ "# Graphistry Tutorial: Notebooks + TigerGraph via raw REST calls\n",
"\n",
"\n",
"* Connect to Graphistry, TigerGraph\n",
@@ -24,8 +22,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "vBYi8AdBwaIB"
},
"source": [
@@ -39,8 +35,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "MApRHslPwjDD"
},
"outputs": [],
@@ -61,8 +55,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "akqSnA63wUF1"
},
"source": [
@@ -78,9 +70,6 @@
"height": 255
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "g0pcHrm0xGFi",
"outputId": "794ccc69-a5bb-49c5-c253-0bd63463b035"
},
@@ -96,8 +85,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "slYnm6W4xJED"
},
"outputs": [],
@@ -115,9 +102,6 @@
"height": 34
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "LDqOCGCFv5hV",
"outputId": "963dff36-8f73-4756-8e0d-af23ff3ea9a3"
},
@@ -142,9 +126,6 @@
"height": 543
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "yr0JmMMmxiXZ",
"outputId": "2d252a88-79f9-4ea1-bc1f-58cc4aef980c"
},
@@ -164,8 +145,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "jlQxZuqeztIP"
},
"source": [
@@ -181,9 +160,6 @@
"height": 255
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "lAGwcRgEz9aF",
"outputId": "5b1c10c7-7a97-4476-f6f3-246926e9aa33"
},
@@ -198,8 +174,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "-JNvt5kYXOLK"
},
"source": [
@@ -213,8 +187,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "fX4NU9ZGH6Ln"
},
"outputs": [],
@@ -277,9 +249,6 @@
"height": 225
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "lnByP_zLXTW9",
"outputId": "11ce110a-5d28-401e-cb9c-c4c73065ae5a"
},
@@ -294,8 +263,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "LUEA1fmFOjCD"
},
"source": [
@@ -311,9 +278,6 @@
"height": 560
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "6kVoTbQt21du",
"outputId": "8b14514f-7a8e-4764-eb28-f379c1b3eec7"
},
@@ -330,8 +294,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "iCeSWQdPbYIC"
},
"source": [
@@ -384,8 +346,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "no9tdOo9oz7r"
},
"source": [
@@ -407,9 +367,6 @@
"height": 359
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "07EGd4ZkLL6F",
"outputId": "6ae6c716-5e51-4f1b-d84e-e9798f8194d6"
},
@@ -428,9 +385,6 @@
"height": 747
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "0RFQ-vVMyiK0",
"outputId": "a5d3ee2c-b5a1-4a11-c726-5524a9005eeb"
},
@@ -450,9 +404,6 @@
"height": 921
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "QBT6jLz4DcBl",
"outputId": "da20c136-4bdb-4795-a367-ab58215fb79a"
},
@@ -465,8 +416,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "fMVqtAhOz0-9"
},
"source": [
@@ -482,9 +431,6 @@
"height": 713
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "1Qzdw9GQ8thS",
"outputId": "ac2970ee-10ad-41fb-d85b-1009c3395804"
},
@@ -501,8 +447,6 @@
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
- "deletable": true,
- "editable": true,
"id": "xRLwg4FDfhBw"
},
"source": [
@@ -518,9 +462,6 @@
"height": 159
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "00eblOG9Zkvc",
"outputId": "b0fdcc4a-a2d2-4c7a-c02d-4854c7c7b015"
},
@@ -543,9 +484,6 @@
"height": 51
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "559q1NaQkiLL",
"outputId": "e4965d2c-c709-4dce-ff93-5d320f12a9e5"
},
@@ -563,9 +501,6 @@
"height": 142
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "tOa2YzcRkl7z",
"outputId": "9f1c1878-004f-47f1-952a-bd74a857c2e4"
},
@@ -596,9 +531,6 @@
"height": 543
},
"colab_type": "code",
- "collapsed": false,
- "deletable": true,
- "editable": true,
"id": "ecMn0Qf_gQSD",
"outputId": "8232bca0-1d6c-4071-f8bc-79d7044fcc98"
},
@@ -623,8 +555,6 @@
"colab": {},
"colab_type": "code",
"collapsed": true,
- "deletable": true,
- "editable": true,
"id": "oogreipqgRTD"
},
"outputs": [],
@@ -646,16 +576,16 @@
"language_info": {
"codemirror_mode": {
"name": "ipython",
- "version": 2
+ "version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
- "pygments_lexer": "ipython2",
- "version": "2.7.13"
+ "pygments_lexer": "ipython3",
+ "version": "3.7.3"
}
},
"nbformat": 4,
- "nbformat_minor": 0
+ "nbformat_minor": 1
}
diff --git a/demos/demos_databases_apis/tigergraph/tigergraph_pygraphistry_bindings.ipynb b/demos/demos_databases_apis/tigergraph/tigergraph_pygraphistry_bindings.ipynb
new file mode 100644
index 0000000000..187ea557b9
--- /dev/null
+++ b/demos/demos_databases_apis/tigergraph/tigergraph_pygraphistry_bindings.ipynb
@@ -0,0 +1,211 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Tigergraph Bindings: Demo of IT Infra Analysis\n",
+ "\n",
+ "Uses bindings built into PyGraphistry for Tigergraph:\n",
+ "\n",
+ "* Configure DB connection\n",
+ "* Call dynamic endpoints for user-defined endpoints\n",
+ "* Call interpreted-mode query\n",
+ "* Visualize results"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Import and connect"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import graphistry\n",
+ "\n",
+ "# !pip install graphistry -q\n",
+ "# graphistry.register(key='...', protocol='https', server='www.acme.com', ...)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "g = graphistry.tigergraph(\n",
+ " protocol='http', server='www.acme.org',\n",
+ " user='tigergraph', pwd='tigergraph', \n",
+ " db='Storage', #optional\n",
+ " #web_port = 14240, api_port = 9000, verbose=True\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Dynamic user-defined GSQL endpoints: Call, analyze, & plot"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# edges: 241\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "g2 = g.gsql_endpoint(\n",
+ " 'StorageImpact', {'vertexType': 'Service', 'input': 61921, 'input.type': 'Pool'},\n",
+ " #{'edges': '@@edgeList', 'nodes': '@@nodeList'}\n",
+ ")\n",
+ "\n",
+ "print('# edges:', len(g2._edges))\n",
+ "\n",
+ "g2.plot()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## On-the-fly GSQL interpreted queries: Call, analyze, & plot"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "# edges: 241\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "g3 = g.gsql(\"\"\"\n",
+ " INTERPRET QUERY () FOR GRAPH Storage { \n",
+ " \n",
+ " OrAccum @@stop;\n",
+ " ListAccum @@edgeList;\n",
+ " SetAccum @@set;\n",
+ " \n",
+ " @@set += to_vertex(\"61921\", \"Pool\");\n",
+ "\n",
+ " Start = @@set;\n",
+ "\n",
+ " while Start.size() > 0 and @@stop == false do\n",
+ "\n",
+ " Start = select t from Start:s-(:e)-:t\n",
+ " where e.goUpper == TRUE\n",
+ " accum @@edgeList += e\n",
+ " having t.type != \"Service\";\n",
+ " end;\n",
+ "\n",
+ " print @@edgeList;\n",
+ " }\n",
+ " \"\"\", \n",
+ " #{'edges': '@@edgeList', 'nodes': '@@nodeList'} # can skip by default\n",
+ ") \n",
+ "\n",
+ "print('# edges:', len(g3._edges))\n",
+ "\n",
+ "g3.plot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.7.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/graphistry/__init__.py b/graphistry/__init__.py
index 331e724b74..ebe3d7ada0 100644
--- a/graphistry/__init__.py
+++ b/graphistry/__init__.py
@@ -2,4 +2,9 @@
__version__ = get_versions()['version']
del get_versions
-from graphistry.pygraphistry import register, bind, edges, nodes, graph, settings, hypergraph, bolt, cypher
+from graphistry.pygraphistry import (
+register, bind, edges, nodes, graph, settings,
+hypergraph,
+bolt, cypher,
+tigergraph, gsql, gsql_endpoint
+)
\ No newline at end of file
diff --git a/graphistry/hyper.py b/graphistry/hyper.py
index 56e9fc793b..d5cc6c30d1 100644
--- a/graphistry/hyper.py
+++ b/graphistry/hyper.py
@@ -105,7 +105,7 @@ def format_hyperedges(events, entity_types, defs, drop_na, drop_edge_attrs):
else [])
+ [defs['EDGETYPE'], defs['ATTRIBID'], defs['EVENTID']]
+ ([defs['CATEGORY']] if is_using_categories else []) ))
- out = pd.concat(subframes, ignore_index=True).reset_index(drop=True)[ result_cols ]
+ out = pd.concat(subframes, ignore_index=True, sort=False).reset_index(drop=True)[ result_cols ]
return out
else:
return pd.DataFrame([])
@@ -168,7 +168,7 @@ def format_hypernodes(events, defs, drop_na):
return event_nodes
def hyperbinding(g, defs, entities, event_entities, edges, source, destination):
- nodes = pd.concat([entities, event_entities], ignore_index=True).reset_index(drop=True)
+ nodes = pd.concat([entities, event_entities], ignore_index=True, sort=False).reset_index(drop=True)
return {
'entities': entities,
'events': event_entities,
@@ -184,7 +184,8 @@ def flatten_objs_inplace(df, cols):
for c in cols:
name = df[c].dtype.name
if name == 'category':
- df[c] = df[c].where(df[c].isnull(), df[c].astype(str))
+ #Avoid warning
+ df[c] = df[c].astype(str).where(~df[c].isnull(), df[c])
elif name == 'object':
df[c] = df[c].where(df[c].isnull(), df[c].astype(str))
diff --git a/graphistry/plotter.py b/graphistry/plotter.py
index dd11a95b3e..3befec3904 100644
--- a/graphistry/plotter.py
+++ b/graphistry/plotter.py
@@ -10,6 +10,8 @@
from .pygraphistry import util
from .pygraphistry import bolt_util
+from .plugins.tigergraph import Tigeristry
+
class Plotter(object):
"""Graph plotting class.
@@ -48,6 +50,7 @@ def __init__(self):
self._url_params = {'info': 'true'}
# Integrations
self._bolt_driver = None
+ self._tigergraph = None
def __repr__(self):
@@ -680,3 +683,165 @@ def cypher(self, query, params={}):
)\
.nodes(nodes)\
.edges(edges)
+
+ def tigergraph(self,
+ protocol = 'http',
+ server = 'localhost',
+ web_port = 14240,
+ api_port = 9000,
+ db = None,
+ user = 'tigergraph',
+ pwd = 'tigergraph',
+ verbose = False
+ ):
+ """Register Tigergraph connection setting defaults
+
+ :param protocol: Protocol used to contact the database.
+ :type protocol: Optional string.
+ :param server: Domain of the database
+ :type server: Optional string.
+ :param web_port:
+ :type web_port: Optional integer.
+ :param api_port:
+ :type api_port: Optional integer.
+ :param db: Name of the database
+ :type db: Optional string.
+ :param user:
+ :type user: Optional string.
+ :param pwd:
+ :type pwd: Optional string.
+ :param verbose: Whether to print operations
+ :type verbose: Optional bool.
+ :returns: Plotter.
+ :rtype: Plotter.
+
+
+ **Example: Standard**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph(protocol='https', server='acme.com', db='my_db', user='alice', pwd='tigergraph2')
+
+ """
+ res = copy.copy(self)
+ res._tigergraph = Tigeristry(self, protocol, server, web_port, api_port, db, user, pwd, verbose)
+ return res
+
+
+ def gsql_endpoint(self, method_name, args = {}, bindings = {}, db = None, dry_run = False):
+ """Invoke Tigergraph stored procedure at a user-definend endpoint and return transformed Plottable
+
+ :param method_name: Stored procedure name
+ :type method_name: String.
+ :param args: Named endpoint arguments
+ :type args: Optional dictionary.
+ :param bindings: Mapping defining names of returned 'edges' and/or 'nodes', defaults to @@nodeList and @@edgeList
+ :type bindings: Optional dictionary.
+ :param db: Name of the database, defaults to value set in .tigergraph(...)
+ :type db: Optional string.
+ :param dry_run: Return target URL without running
+ :type dry_run: Bool, defaults to False
+ :returns: Plotter.
+ :rtype: Plotter.
+
+ **Example: Minimal**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph(db='my_db')
+ tg.gsql_endpoint('neighbors').plot()
+
+ **Example: Full**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ tg.gsql_endpoint('neighbors', {'k': 2}, {'edges': 'my_edge_list'}, 'my_db').plot()
+
+ **Example: Read data**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ out = tg.gsql_endpoint('neighbors')
+ (nodes_df, edges_df) = (out._nodes, out._edges)
+
+ """
+ return self._tigergraph.gsql_endpoint(self, method_name, args, bindings, db, dry_run)
+
+ def gsql(self, query, bindings = {}, dry_run = False):
+ """Run Tigergraph query in interpreted mode and return transformed Plottable
+
+ :param query: Code to run
+ :type query: String.
+ :param bindings: Mapping defining names of returned 'edges' and/or 'nodes', defaults to @@nodeList and @@edgeList
+ :type bindings: Optional dictionary.
+ :param dry_run: Return target URL without running
+ :type dry_run: Bool, defaults to False
+ :returns: Plotter.
+ :rtype: Plotter.
+
+ **Example: Minimal**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ tg.gsql(\"\"\"
+INTERPRET QUERY () FOR GRAPH Storage {
+
+ OrAccum @@stop;
+ ListAccum @@edgeList;
+ SetAccum @@set;
+
+ @@set += to_vertex("61921", "Pool");
+
+ Start = @@set;
+
+ while Start.size() > 0 and @@stop == false do
+
+ Start = select t from Start:s-(:e)-:t
+ where e.goUpper == TRUE
+ accum @@edgeList += e
+ having t.type != "Service";
+ end;
+
+ print @@edgeList;
+ }
+ \"\"\").plot()
+
+ **Example: Full**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ tg.gsql(\"\"\"
+INTERPRET QUERY () FOR GRAPH Storage {
+
+ OrAccum @@stop;
+ ListAccum @@edgeList;
+ SetAccum @@set;
+
+ @@set += to_vertex("61921", "Pool");
+
+ Start = @@set;
+
+ while Start.size() > 0 and @@stop == false do
+
+ Start = select t from Start:s-(:e)-:t
+ where e.goUpper == TRUE
+ accum @@edgeList += e
+ having t.type != "Service";
+ end;
+
+ print @@my_edge_list;
+ }
+ \"\"\", {'edges': 'my_edge_list'}).plot()
+ """
+ return self._tigergraph.gsql(self, query, bindings, dry_run)
+
+
+
+
+
+
+
diff --git a/graphistry/plugins/__init__.py b/graphistry/plugins/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/graphistry/plugins/tigergraph.py b/graphistry/plugins/tigergraph.py
new file mode 100644
index 0000000000..c70e014a48
--- /dev/null
+++ b/graphistry/plugins/tigergraph.py
@@ -0,0 +1,199 @@
+import requests
+import pandas as pd
+
+def merge_dicts(x, y):
+ return dict(list(x.items()) + list(y.items()))
+
+class Tigeristry(object):
+ """Tigergraph bindings class
+
+ * Initialize with DB cfg
+ * Register named stored procedures and graphistry bindings
+ * Call stored procedures
+ * Call interpreted queries
+
+ """
+
+ # ----------------- Helpers -----------------------
+
+ def __log(self, v):
+ if self.tiger_config['verbose']:
+ print(v)
+
+ # () -> 'http://site.com:9000'
+ def __base_url(self, mode = 'api'):
+ port = self.tiger_config['web_port'] if mode == 'web' else self.tiger_config['api_port']
+ who = \
+ (self.tiger_config['user'] + ':' + self.tiger_config['pwd'] + '@') \
+ if (not (self.tiger_config['user'] is None) and not (self.tiger_config['pwd'] is None)) \
+ else ''
+ return self.tiger_config['protocol'] + '://'+ who + self.tiger_config['server'] + ':' + str(port)
+
+ def __check_initialized(self, graphistry):
+ if (graphistry is None) or (graphistry._tigergraph is None):
+ raise Exception("First register a tigergraph db via .tigergraph() or .register(tigergraph=)")
+
+
+ # --------------------------------------------------
+
+
+ def __init__(
+ self,
+ graphistry,
+ protocol = 'http',
+ server = 'localhost',
+ web_port = 14240,
+ api_port = 9000,
+ db = None,
+ user = 'tigergraph',
+ pwd = 'tigergraph',
+ verbose = False
+ ):
+
+ self.tiger_config = {
+ 'protocol': protocol,
+ 'server': server,
+ 'web_port': web_port,
+ 'api_port': api_port,
+ 'db': db,
+ 'user': user,
+ 'pwd': pwd,
+ 'verbose': verbose
+ }
+
+ self.__log('TG config: ' + str({k: v for k, v in self.tiger_config.items() if k not in ['pwd']}))
+
+
+ # --------------------------------------------------
+
+ def __verify_and_unwrap_json_result(self, json):
+
+ if json is None:
+ raise Exception("No response!")
+ elif not 'error' in json:
+ raise Exception("Unexpected response format, no validity indicator", json)
+ elif json['error']:
+ raise Exception("Database returned error", json['message'] if 'message' in json else 'No message')
+ elif not ('results' in json):
+ raise Exception("No field results in database response")
+
+ return json['results']
+
+ # str * ?dict * ?str => json graph
+ def __gsql_endpoint(self, method_name, args = {}, db = None, dry_run = False):
+
+ db = self.tiger_config['db'] if db is None else db
+ if db is None:
+ raise Exception("Must specify db in Tigeristry constructor or .__call()")
+
+ base_url = self.__base_url('api')
+ url = base_url + '/query/' + db + '/' + method_name
+ if len(args.items()) > 0:
+ url = url + '?' + '&'.join( [str(k) + '=' + str(v) for k, v in args.items()] )
+ self.__log(url)
+
+ if dry_run:
+ return url
+
+ resp = requests.get(url)
+ self.__log(resp)
+ json = resp.json()
+
+ return self.__verify_and_unwrap_json_result(json)
+
+ def __json_to_graphistry(self, graphistry, json, bindings):
+ edges_df = pd.DataFrame({'from_id': [], 'to_id': []})
+ edge_key = bindings['edges']
+ edges = [x for x in json if edge_key in x]
+ if len(edges) > 0 and (edge_key in edges[0]):
+ edges = edges[0][edge_key]
+ edges_df = pd.DataFrame(edges)
+ try:
+ edges_df = edges_df.drop(columns=['attributes'])
+ attrs = [x['attributes'] for x in edges]
+ edges_df = pd.merge( edges_df, pd.DataFrame(attrs), left_index=True, right_index=True )
+ except:
+ self.__log('Failed to extract edge attrs')
+ g = graphistry.bind(source='from_id', destination='to_id').edges(edges_df)
+
+ nodes_df = pd.DataFrame({'type': [], 'node_id': []})
+ node_key = bindings['nodes']
+ nodes = [x for x in json if node_key in x]
+ if len(nodes) > 0 and (node_key in nodes[0]):
+ nodes = nodes[0][node_key]
+ nodes_df = pd.DataFrame(nodes)
+ try:
+ nodes_df = nodes_df.drop(columns=['attributes'])
+ attrs = [x['attributes'] for x in nodes]
+ nodes_df = pd.merge( nodes_df, pd.DataFrame(attrs), left_index=True, right_index=True )
+ except:
+ self.__log('Failed to extract node attrs')
+ else:
+ nodes_df = pd.DataFrame({'node_id': edges_df['from_id'].append(edges_df['to_id'])}) \
+ .drop_duplicates().reset_index(drop=True)
+ from_types = nodes_df.merge(edges_df[['from_id', 'from_type']].rename(columns={'from_id': 'node_id', 'from_type': 'type'}), on='node_id', how='left')
+ to_types = nodes_df.merge(edges_df[['to_id', 'to_type']].rename(columns={'to_id': 'node_id', 'to_type': 'type'}), on='node_id', how='left')
+ nodes_df = nodes_df.merge(
+ pd.DataFrame({'type':
+ from_types.merge(to_types, left_index=True, right_index=True)\
+ .apply(
+ lambda row: row['type_x'] if not pd.isna(row['type_x']) else row['type_y'],
+ axis=1)}),
+ left_index=True, right_index=True)
+ g = g.bind(node='node_id').nodes(nodes_df)
+ return g
+
+ def __gsql(self, query, dry_run = False):
+ base_url = self.__base_url('web')
+ url = base_url + '/gsqlserver/interpreted_query'
+ self.__log(url)
+ if dry_run == True:
+ return url
+ response = requests.post(url, data=query)
+ json = response.json()
+ return self.__verify_and_unwrap_json_result(json)
+
+
+ # --------------------------------------------------
+
+ # Tigeristry * Plotter * string * ?dict * ?dict * ?string => Plotter
+ def gsql_endpoint(self, graphistry, method_name, args = {}, bindings = {}, db = None, dry_run = False):
+
+ self.__check_initialized(graphistry)
+
+ json = self.__gsql_endpoint(method_name, args, db, dry_run)
+
+ if dry_run == True:
+ url = json
+ return url
+
+ bindings = merge_dicts(
+ {
+ 'edges': '@@edgeList',
+ 'nodes': '@@nodeList'
+ },
+ bindings
+ )
+
+ return self.__json_to_graphistry(graphistry, json, bindings)
+
+ # Tigeristry * Plotter * string * ?dict => Plotter
+ def gsql(self, graphistry, query, bindings = {}, dry_run = False):
+
+ self.__check_initialized(graphistry)
+
+ json = self.__gsql(query, dry_run)
+
+ if dry_run == True:
+ url = json
+ return url
+
+ bindings = merge_dicts(
+ {
+ 'edges': '@@edgeList',
+ 'nodes': '@@nodeList'
+ },
+ bindings
+ )
+
+ return self.__json_to_graphistry(graphistry, json, bindings)
\ No newline at end of file
diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py
index 782a17b110..f0a7dd907c 100644
--- a/graphistry/pygraphistry.py
+++ b/graphistry/pygraphistry.py
@@ -345,6 +345,169 @@ def bind(node=None, source=None, destination=None,
point_title, point_label, point_color, point_size)
+ @staticmethod
+ def tigergraph(
+ protocol = 'http',
+ server = 'localhost',
+ web_port = 14240,
+ api_port = 9000,
+ db = None,
+ user = 'tigergraph',
+ pwd = 'tigergraph',
+ verbose = False
+ ):
+ """Register Tigergraph connection setting defaults
+
+ :param protocol: Protocol used to contact the database.
+ :type protocol: Optional string.
+ :param server: Domain of the database
+ :type server: Optional string.
+ :param web_port:
+ :type web_port: Optional integer.
+ :param api_port:
+ :type api_port: Optional integer.
+ :param db: Name of the database
+ :type db: Optional string.
+ :param user:
+ :type user: Optional string.
+ :param pwd:
+ :type pwd: Optional string.
+ :param verbose: Whether to print operations
+ :type verbose: Optional bool.
+ :returns: Plotter.
+ :rtype: Plotter.
+
+
+ **Example: Standard**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph(protocol='https', server='acme.com', db='my_db', user='alice', pwd='tigergraph2')
+
+ """
+ from . import plotter
+ return plotter.Plotter().tigergraph(protocol, server, web_port, api_port, db, user, pwd, verbose)
+
+
+ @staticmethod
+ def gsql_endpoint(self, method_name, args = {}, bindings = None, db = None, dry_run = False):
+ """Invoke Tigergraph stored procedure at a user-definend endpoint and return transformed Plottable
+
+ :param method_name: Stored procedure name
+ :type method_name: String.
+ :param args: Named endpoint arguments
+ :type args: Optional dictionary.
+ :param bindings: Mapping defining names of returned 'edges' and/or 'nodes', defaults to @@nodeList and @@edgeList
+ :type bindings: Optional dictionary.
+ :param db: Name of the database, defaults to value set in .tigergraph(...)
+ :type db: Optional string.
+ :param dry_run: Return target URL without running
+ :type dry_run: Bool, defaults to False
+ :returns: Plotter.
+ :rtype: Plotter.
+
+ **Example: Minimal**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph(db='my_db')
+ tg.gsql_endpoint('neighbors').plot()
+
+ **Example: Full**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ tg.gsql_endpoint('neighbors', {'k': 2}, {'edges': 'my_edge_list'}, 'my_db').plot()
+
+ **Example: Read data**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ out = tg.gsql_endpoint('neighbors')
+ (nodes_df, edges_df) = (out._nodes, out._edges)
+
+ """
+ from . import plotter
+ return plotter.Plotter().gsql_endpoint(method_name, args, bindings, db, dry_run)
+
+
+
+ @staticmethod
+ def gsql(query, bindings = None, dry_run = False):
+ """Run Tigergraph query in interpreted mode and return transformed Plottable
+
+ :param query: Code to run
+ :type query: String.
+ :param bindings: Mapping defining names of returned 'edges' and/or 'nodes', defaults to @@nodeList and @@edgeList
+ :type bindings: Optional dictionary.
+ :param dry_run: Return target URL without running
+ :type dry_run: Bool, defaults to False
+ :returns: Plotter.
+ :rtype: Plotter.
+
+ **Example: Minimal**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ tg.gsql(\"\"\"
+INTERPRET QUERY () FOR GRAPH Storage {
+
+ OrAccum @@stop;
+ ListAccum @@edgeList;
+ SetAccum @@set;
+
+ @@set += to_vertex("61921", "Pool");
+
+ Start = @@set;
+
+ while Start.size() > 0 and @@stop == false do
+
+ Start = select t from Start:s-(:e)-:t
+ where e.goUpper == TRUE
+ accum @@edgeList += e
+ having t.type != "Service";
+ end;
+
+ print @@edgeList;
+ }
+ \"\"\").plot()
+
+ **Example: Full**
+ ::
+
+ import graphistry
+ tg = graphistry.tigergraph()
+ tg.gsql(\"\"\"
+INTERPRET QUERY () FOR GRAPH Storage {
+
+ OrAccum @@stop;
+ ListAccum @@edgeList;
+ SetAccum @@set;
+
+ @@set += to_vertex("61921", "Pool");
+
+ Start = @@set;
+
+ while Start.size() > 0 and @@stop == false do
+
+ Start = select t from Start:s-(:e)-:t
+ where e.goUpper == TRUE
+ accum @@edgeList += e
+ having t.type != "Service";
+ end;
+
+ print @@my_edge_list;
+ }
+ \"\"\", {'edges': 'my_edge_list'}).plot()
+ """
+ from . import plotter
+ return plotter.Plotter().gsql(query, bindings, dry_run)
+
+
+
@staticmethod
def nodes(nodes):
from . import plotter
@@ -544,6 +707,9 @@ def _check_key_and_version():
hypergraph = PyGraphistry.hypergraph
bolt = PyGraphistry.bolt
cypher = PyGraphistry.cypher
+tigergraph = PyGraphistry.tigergraph
+gsql_endpoint = PyGraphistry.gsql_endpoint
+gsql = PyGraphistry.gsql
class NumpyJSONEncoder(json.JSONEncoder):
diff --git a/graphistry/tests/test_tigergraph.py b/graphistry/tests/test_tigergraph.py
new file mode 100644
index 0000000000..795dc606f9
--- /dev/null
+++ b/graphistry/tests/test_tigergraph.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+import graphistry
+from mock import patch
+from common import NoAuthTestCase
+
+class TestTiger(NoAuthTestCase):
+ def test_tg_init_plain(self):
+ tg = graphistry.tigergraph()
+ self.assertTrue(type(tg) == graphistry.plotter.Plotter)
+
+ def test_tg_init_many(self):
+ tg = graphistry.tigergraph(
+ protocol = 'https',
+ server = '127.0.0.1',
+ web_port = 10000,
+ api_port = 11000,
+ db = 'z',
+ user = 'tigergraph1',
+ pwd = 'tigergraph2',
+ verbose = False
+ )
+ self.assertTrue(type(tg) == graphistry.plotter.Plotter)
+
+ def test_tg_endpoint_url_simple(self):
+ tg = graphistry.tigergraph(
+ protocol = 'https',
+ server = '127.0.0.1',
+ web_port = 10000,
+ api_port = 11000,
+ db = 'z',
+ user = 'tigergraph1',
+ pwd = 'tigergraph2',
+ verbose = False
+ )
+ self.assertEqual(
+ tg.gsql_endpoint('x', dry_run = True),
+ 'https://tigergraph1:tigergraph2@127.0.0.1:11000/query/z/x'
+ )
+
+ def test_tg_endpoint_url_1_arg(self):
+ tg = graphistry.tigergraph(
+ protocol = 'https',
+ server = '127.0.0.1',
+ web_port = 10000,
+ api_port = 11000,
+ db = 'z',
+ user = 'tigergraph1',
+ pwd = 'tigergraph2',
+ verbose = False
+ )
+ self.assertEqual(
+ tg.gsql_endpoint('x', {'f': 1}, dry_run = True),
+ 'https://tigergraph1:tigergraph2@127.0.0.1:11000/query/z/x?f=1'
+ )
+
+ def test_tg_endpoint_url_3_arg(self):
+ tg = graphistry.tigergraph(
+ protocol = 'https',
+ server = '127.0.0.1',
+ web_port = 10000,
+ api_port = 11000,
+ db = 'z',
+ user = 'tigergraph1',
+ pwd = 'tigergraph2',
+ verbose = False
+ )
+ #27 does not preserve order
+ self.assertEqual(
+ len(tg.gsql_endpoint('x', {'f': 1, 'ggg': 2, 'h': 33}, dry_run = True)),
+ len('https://tigergraph1:tigergraph2@127.0.0.1:11000/query/z/x?f=1&ggg=2&h=33')
+ )
+
+ def test_tg_gsql(self):
+ tg = graphistry.tigergraph(
+ protocol = 'https',
+ server = '127.0.0.1',
+ web_port = 10000,
+ api_port = 11000,
+ db = 'z',
+ user = 'tigergraph1',
+ pwd = 'tigergraph2',
+ verbose = False
+ )
+ self.assertEqual(
+ tg.gsql('x', dry_run = True),
+ 'https://tigergraph1:tigergraph2@127.0.0.1:10000/gsqlserver/interpreted_query'
+ )
+