diff --git a/ChangeLog.md b/ChangeLog.md index 4e27c3ab..d2893f1c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -13,6 +13,7 @@ Starting with v1.31.6, this file will contain a record of major features and upd - Suppressed default root logger error output ([Link to PR](https://github.com/aws/graph-notebook/pull/248)) - Fixed Gremlin visualizer bug with handling non-string node IDs ([Link to PR](https://github.com/aws/graph-notebook/pull/245)) - Fixed error in openCypher Bolt query metadata output ([Link to PR](https://github.com/aws/graph-notebook/pull/255)) +- Fixed handling of Decimal type properties when rendering Gremlin query results ([Link to PR](https://github.com/aws/graph-notebook/pull/256)) ## Release 3.1.1 (December 21, 2021) - Added new dataset for DiningByFriends, and associated notebook ([Link to PR](https://github.com/aws/graph-notebook/pull/235)) diff --git a/src/graph_notebook/network/gremlin/GremlinNetwork.py b/src/graph_notebook/network/gremlin/GremlinNetwork.py index 2259367a..f9ea4780 100644 --- a/src/graph_notebook/network/gremlin/GremlinNetwork.py +++ b/src/graph_notebook/network/gremlin/GremlinNetwork.py @@ -7,6 +7,7 @@ import json import uuid import logging +from decimal import * from enum import Enum from graph_notebook.network.EventfulNetwork import EventfulNetwork, DEFAULT_GRP, DEPTH_GRP_KEY, DEFAULT_RAW_GRP_KEY @@ -435,7 +436,20 @@ def add_vertex(self, v, path_index: int = -1): for k in v: if str(k) == T_ID: node_id = str(v[k]) - properties[k] = str(v[k]) if isinstance(v[k], dict) else v[k] + + if isinstance(v[k], dict): + properties[k] = str(v[k]) + elif isinstance(v[k], list): + copy_val = v[k] + for i, subvalue in enumerate(copy_val): + if isinstance(subvalue, Decimal): + copy_val[i] = float(subvalue) + properties[k] = copy_val + elif isinstance(v[k], Decimal): + properties[k] = float(v[k]) + else: + properties[k] = v[k] + if not group_is_set: if isinstance(self.group_by_property, dict): try: @@ -571,10 +585,14 @@ def add_path_edge(self, edge, from_id='', to_id='', data=None): for k in edge: if str(k) == T_ID: edge_id = str(edge[k]) - if type(edge[k]) is dict: # Handle Direction properties, where the value is a map + + if isinstance(edge[k], dict): # Handle Direction properties, where the value is a map properties[k] = get_id(edge[k]) + elif isinstance(edge[k], Decimal): + properties[k] = float(edge[k]) else: properties[k] = edge[k] + if self.edge_display_property is not T_LABEL and not display_is_set: label_property_raw_value = self.get_dict_element_property_value(edge, k, edge_title_plc, self.edge_display_property) diff --git a/test/unit/network/gremlin/test_gremlin_network.py b/test/unit/network/gremlin/test_gremlin_network.py index 9c46ce9c..2c3b47d4 100644 --- a/test/unit/network/gremlin/test_gremlin_network.py +++ b/test/unit/network/gremlin/test_gremlin_network.py @@ -4,6 +4,7 @@ """ import unittest +from decimal import Decimal from gremlin_python.structure.graph import Path, Edge, Vertex from gremlin_python.process.traversal import T, Direction from graph_notebook.network.EventfulNetwork import EVENT_ADD_NODE @@ -710,6 +711,48 @@ def test_add_vertex_with_valid_label_and_invalid_tooltip(self): self.assertEqual(node['label'], 'SEA') self.assertEqual(node['title'], 'SEA') + def test_add_vertex_with_Decimal_type_property(self): + vertex = { + T.id: '1234', + T.label: 'airport', + 'type': 'Airport', + 'runways': '4', + 'code': 'SEA', + 'lon': Decimal('-21.940599441500001631766281207092106342315673828125'), + 'lat': Decimal('64.129997253400006229639984667301177978515625') + } + + gn = GremlinNetwork() + gn.add_vertex(vertex) + node = gn.graph.nodes.get(vertex[T.id]) + final_lon_value = node['properties']['lon'] + final_lat_value = node['properties']['lat'] + self.assertEqual(final_lon_value, -21.9405994415) + self.assertEqual(final_lat_value, 64.1299972534) + self.assertIsInstance(final_lon_value, float) + self.assertIsInstance(final_lat_value, float) + + def test_add_vertex_with_Decimal_type_property_in_list(self): + vertex = { + T.id: '1234', + T.label: 'airport', + 'type': ['Airport'], + 'runways': ['4'], + 'code': ['SEA'], + 'lon': [Decimal('-21.940599441500001631766281207092106342315673828125')], + 'lat': [Decimal('64.129997253400006229639984667301177978515625')] + } + + gn = GremlinNetwork() + gn.add_vertex(vertex) + node = gn.graph.nodes.get(vertex[T.id]) + final_lon_value = node['properties']['lon'][0] + final_lat_value = node['properties']['lat'][0] + self.assertEqual(final_lon_value, -21.9405994415) + self.assertEqual(final_lat_value, 64.1299972534) + self.assertIsInstance(final_lon_value, float) + self.assertIsInstance(final_lat_value, float) + def test_add_explicit_type_single_edge_without_edge_property(self): vertex1 = Vertex(id='1') vertex2 = Vertex(id='2') @@ -1402,6 +1445,22 @@ def test_add_single_edge_with_valid_label_and_invalid_tooltip(self): self.assertEqual(edge['1']['label'], 'v[2]') self.assertEqual(edge['1']['title'], 'v[2]') + def test_add_edge_with_decimal_property(self): + vertex1 = Vertex(id='1') + vertex2 = Vertex(id='2') + + edge1 = {T.id: '1', T.label: 'route', 'outV': 'v[1]', 'inV': 'v[2]', + 'dist': Decimal('917.09438902349805490380928798847027304002305757893')} + + gn = GremlinNetwork() + gn.add_vertex(vertex1) + gn.add_vertex(vertex2) + gn.add_path_edge(edge1, from_id='1', to_id='2') + edge = gn.graph.get_edge_data('1', '2') + edge_dist = edge['1']['properties']['dist'] + self.assertEqual(edge_dist, 917.094389023498) + self.assertIsInstance(edge_dist, float) + def test_add_path_with_integer(self): path = Path([], ['ANC', 3030, 'DFW']) gn = GremlinNetwork() @@ -1863,13 +1922,13 @@ def test_add_path_without_groupby(self): def test_add_path_with_groupby(self): path = Path([], [{'country': ['US'], 'code': ['SEA'], 'longest': [11901], 'city': ['Seattle'], - T.label: 'airport', 'lon': [-122.30899810791], 'type': ['airport'], 'elev': [432], + T.label: 'airport', 'lon': ['-122.30899810791'], 'type': ['airport'], 'elev': [432], T.id: '22', 'icao': ['KSEA'], 'runways': [3], 'region': ['US-WA'], - 'lat': [47.4490013122559], 'desc': ['Seattle-Tacoma']}, + 'lat': ['47.4490013122559'], 'desc': ['Seattle-Tacoma']}, {'country': ['US'], 'code': ['ATL'], 'longest': [12390], 'city': ['Atlanta'], - T.label: 'airport', 'lon': [-84.4281005859375], 'type': ['airport'], 'elev': [1026], + T.label: 'airport', 'lon': ['-84.4281005859375'], 'type': ['airport'], 'elev': [1026], T.id: '1', 'icao': ['KATL'], 'runways': [5], 'region': ['US-GA'], - 'lat': [33.6366996765137], 'desc': ['Hartsfield - Jackson Atlanta International Airport']}]) + 'lat': ['33.6366996765137'], 'desc': ['Hartsfield - Jackson Atlanta International Airport']}]) gn = GremlinNetwork(group_by_property="code") gn.add_results([path]) node = gn.graph.nodes.get('1')