From 7617905c20e97a1c0ef4b5089ab0fb65887454b9 Mon Sep 17 00:00:00 2001 From: Christian Legaspi <1392774+clegaspi@users.noreply.github.com> Date: Wed, 24 Jul 2019 12:02:20 -0700 Subject: [PATCH 1/5] Add functionality to download PNG file of graph, switch to custom dash-cytoscape --- dash-cytoscape | 2 +- propnet/web/layouts_explore.py | 43 ++++++++++++++++++++++++++-------- requirements.txt | 6 +++-- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/dash-cytoscape b/dash-cytoscape index e1f25c14..6d32a857 160000 --- a/dash-cytoscape +++ b/dash-cytoscape @@ -1 +1 @@ -Subproject commit e1f25c14aaa4900685876dfbcddcfe5516ae7ebe +Subproject commit 6d32a857e7643e87b66cfa2d818de18152d421c8 diff --git a/propnet/web/layouts_explore.py b/propnet/web/layouts_explore.py index 30bdc92f..c6e75b9f 100644 --- a/propnet/web/layouts_explore.py +++ b/propnet/web/layouts_explore.py @@ -1,5 +1,6 @@ import dash_core_components as dcc import dash_html_components as html +from dash.exceptions import PreventUpdate from dash.dependencies import Input, Output, State @@ -25,13 +26,19 @@ def explore_layout(app): graph_layout = html.Div( id='graph_top_level', children=[ - dcc.Checklist(id='graph_options', - options=[{'label': 'Show models', - 'value': 'show_models'}, - {'label': 'Show properties', - 'value': 'show_properties'}], - value=['show_properties'], - labelStyle={'display': 'inline-block'}), + html.Div([ + dcc.Checklist(id='graph_options', + options=[{'label': 'Show models', + 'value': 'show_models'}, + {'label': 'Show properties', + 'value': 'show_properties'}], + value=['show_properties'], + labelStyle={'display': 'inline-block'}, + style={'display': 'inline-block'}), + html.Button('Download PNG', id='download-png', + style={'display': 'inline-block', + 'margin-left': '10px'}) + ]), html.Div(id='graph_explorer', children=[graph_component])]) @@ -46,8 +53,24 @@ def change_propnet_graph_label_selection(props, elements): return elements - layout = html.Div([html.Div([graph_layout], className='row'), - html.Div([html.Div([models_index], className='six columns'), - html.Div([symbols_index()], className='six columns'),], className='row')]) + @app.callback(Output('pn-graph', 'generateImage'), + [Input('download-png', 'n_clicks')]) + def download_image(n_clicks): + if n_clicks is None: + raise PreventUpdate + return { + 'type': 'png', + 'action': 'download', + 'filename': 'pngraph' + } + + layout = html.Div([ + html.Div([graph_layout], className='row'), + html.Div([ + html.Div([models_index], className='six columns'), + html.Div([symbols_index()], className='six columns')], + className='row'), + html.Div(id='emptydiv') + ]) return layout diff --git a/requirements.txt b/requirements.txt index ec3258d6..31cb4e9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +-e dash-cytoscape + # core, utilities numpy>=1.15.1 scipy>=1.0.1 @@ -17,7 +19,7 @@ pandas>=0.23.4 dash==1.0.1 gunicorn>=19.7.1 Flask-Caching>=1.3.3 -dash-cytoscape==0.1.1 +# dash-cytoscape==0.1.1 # Sub for custom package # references habanero>=0.6.0 @@ -41,4 +43,4 @@ minepy>=1.2.3 # specific models gbml>=1.1.0 --e . \ No newline at end of file +-e . From c121139d2281d1df14bb71813bbe9fb5a16af6ca Mon Sep 17 00:00:00 2001 From: Christian Legaspi <1392774+clegaspi@users.noreply.github.com> Date: Wed, 24 Jul 2019 12:14:37 -0700 Subject: [PATCH 2/5] Remove dash-cytoscape submodule --- .gitmodules | 3 --- dash-cytoscape | 1 - 2 files changed, 4 deletions(-) delete mode 160000 dash-cytoscape diff --git a/.gitmodules b/.gitmodules index 5154476f..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "dash-cytoscape"] - path = dash-cytoscape - url = https://github.com/clegaspi/dash-cytoscape.git diff --git a/dash-cytoscape b/dash-cytoscape deleted file mode 160000 index 6d32a857..00000000 --- a/dash-cytoscape +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6d32a857e7643e87b66cfa2d818de18152d421c8 From dbdd5885582e0add0f77e1d5c327fc18c130b54c Mon Sep 17 00:00:00 2001 From: Christian Legaspi <1392774+clegaspi@users.noreply.github.com> Date: Wed, 24 Jul 2019 12:36:58 -0700 Subject: [PATCH 3/5] Point dash-cytoscape at personal repository for custom version with image download feature --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 31cb4e9a..8115d33d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ --e dash-cytoscape +-e git+https://github.com/clegaspi/dash-cytoscape.git#egg=dash-cytoscape # core, utilities numpy>=1.15.1 From 771ddd871dd84a53006ce661a0d33b78e8798606 Mon Sep 17 00:00:00 2001 From: Christian Legaspi <1392774+clegaspi@users.noreply.github.com> Date: Wed, 24 Jul 2019 12:39:21 -0700 Subject: [PATCH 4/5] Add custom dash-cytoscape to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 5f8d603e..e586d3f6 100644 --- a/.gitignore +++ b/.gitignore @@ -126,3 +126,6 @@ propnet/core/alternate_graph_algorithms/graph_parallel_recursive.py propnet/core/alternate_graph_algorithms/graph_two_threads.py propnet/core/alternate_graph_algorithms/old_graph.py mpds-api/ + +# custom dash-cytoscape +src/ From c666ba7c865908910ab2c530ddb5713651d293b2 Mon Sep 17 00:00:00 2001 From: Christian Legaspi <1392774+clegaspi@users.noreply.github.com> Date: Wed, 24 Jul 2019 12:40:34 -0700 Subject: [PATCH 5/5] Web hacks for knowledge graph figure creation. WIP to refactor. --- propnet/web/graph_layout_config.yaml | 4 +- propnet/web/graph_settings.yaml | 2 +- propnet/web/layouts_explore.py | 64 +++++++++++++++++++++++++++- propnet/web/utils.py | 26 +++++++---- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/propnet/web/graph_layout_config.yaml b/propnet/web/graph_layout_config.yaml index 90b34972..23aceb6b 100644 --- a/propnet/web/graph_layout_config.yaml +++ b/propnet/web/graph_layout_config.yaml @@ -5,8 +5,8 @@ fit: true ungrabifyWhileSimulating: true maxIterations: 100000 maxSimulationTime: 3000 -springCoeff: 0.04 -springLength: 140 +springCoeff: 0.2 +springLength: 100 gravity: -3000 pull: 0.25 theta: 0.9 diff --git a/propnet/web/graph_settings.yaml b/propnet/web/graph_settings.yaml index b1dc6a5c..cfbb1e76 100644 --- a/propnet/web/graph_settings.yaml +++ b/propnet/web/graph_settings.yaml @@ -4,7 +4,7 @@ full_view: maxZoom: 2 style: width: "100%" - height: "800px" + height: "1200px" border: "1px #BDBDBD solid" model_symbol_view: boxSelectionEnabled: true diff --git a/propnet/web/layouts_explore.py b/propnet/web/layouts_explore.py index c6e75b9f..5efeb425 100644 --- a/propnet/web/layouts_explore.py +++ b/propnet/web/layouts_explore.py @@ -11,10 +11,68 @@ from propnet.web.layouts_models import models_index from propnet.web.layouts_symbols import symbols_index +from propnet.core.registry import Registry +models_to_show = [ + 'cost', + 'hhi', + 'magnetization_normalized_volume', + 'molar_mass_from_formula', + 'gbml', + 'density_relations', + 'pymatgen_structure_properties', + 'clarke_thermal_conductivity', + 'voigt_bulk_modulus', + 'hill_bulk_modulus', + 'reuss_bulk_modulus', + 'compliance_from_elasticity', + 'piezoelectric_tensor', + 'electromechanical_coupling', + 'homogeneous_elasticity_relations', + 'debye_temperature', + 'sound_velocity_elastic_longitudinal', + 'sound_velocity_elastic_transverse', + 'sound_velocity_elastic_mean' + ] +symbols_to_show = [ + 'hhi_production', + 'hhi_reserve', + 'cost_per_kg', + 'cost_per_mol', + 'total_magnetization_per_volume', + 'total_magnetization', + 'molar_mass', + 'volume_unit_cell', + 'volume_per_atom', + 'lattice', + 'composition', + 'nsites', + 'mass_per_atom', + 'density', + 'computed_entry', + 'formula', + 'youngs_modulus', + 'bulk_modulus', + 'compliance_tensor_voigt', + 'elastic_tensor_voigt', + 'piezoelectric_tensor', + 'piezoelectric_tensor_converse', + 'electromechanical_coupling', + 'thermal_conductivity', + 'debye_temperature', + 'sound_velocity_longitudinal', + 'sound_velocity_transverse', + 'sound_velocity_mean', + ] +labels = [Registry("models")[v] for v in models_to_show] + [Registry("symbols")[v] for v in symbols_to_show] def explore_layout(app): - graph_data = graph_conversion(propnet_nx_graph, hide_unconnected_nodes=False) + # graph_data = graph_conversion(propnet_nx_graph, hide_unconnected_nodes=False) + + graph_data = graph_conversion(propnet_nx_graph, hide_unconnected_nodes=True, + labels_to_show=labels, + show_symbol_labels=False, + show_model_labels=False) graph_component = html.Div( id='graph_component', children=[Cytoscape(id='pn-graph', elements=graph_data, @@ -49,7 +107,9 @@ def change_propnet_graph_label_selection(props, elements): show_properties = 'show_properties' in props show_models = 'show_models' in props - update_labels(elements, show_models=show_models, show_symbols=show_properties) + # update_labels(elements, show_models=show_models, show_symbols=show_properties) + update_labels(elements, show_models=show_models, show_symbols=show_properties, + models_to_show=models_to_show, symbols_to_show=symbols_to_show) return elements diff --git a/propnet/web/utils.py b/propnet/web/utils.py index 288e3d27..a6e10c2d 100644 --- a/propnet/web/utils.py +++ b/propnet/web/utils.py @@ -3,6 +3,7 @@ from os import path import re from urllib.parse import parse_qs, urlsplit +from pydash import get from propnet.core.symbols import Symbol from propnet.core.models import Model @@ -34,7 +35,8 @@ def graph_conversion(graph: nx.DiGraph, derivation_pathway=None, hide_unconnected_nodes=True, show_symbol_labels=True, - show_model_labels=False): + show_model_labels=False, + labels_to_show=None): """Utility function to render a networkx graph from Graph.graph for use in GraphComponent @@ -54,12 +56,12 @@ def graph_conversion(graph: nx.DiGraph, # TODO: more dumb crap related to graph if isinstance(n, Symbol): # property - name = n.name + name = 'symbol_' + n.name label = n.display_names[0] node_type = 'symbol' elif isinstance(n, Model): # model - name = n.title + name = 'model_' + n.name label = n.title node_type = 'model' else: @@ -78,7 +80,8 @@ def graph_conversion(graph: nx.DiGraph, } if (node_type == 'model' and show_model_labels) or \ - (node_type == 'symbol' and show_symbol_labels): + (node_type == 'symbol' and show_symbol_labels) or \ + (labels_to_show and n in labels_to_show): node['classes'].append('label-on') else: node['classes'].append('label-off') @@ -89,7 +92,7 @@ def graph_conversion(graph: nx.DiGraph, # TODO: need to clean up after model refactor def get_node_id(node_): - return node_.title if isinstance(node_, Model) else node_.name + return 'model_' + node_.name if isinstance(node_, Model) else 'symbol_' + node_.name for n1, n2 in graph.edges(): id_n1 = get_node_id(n1) @@ -105,7 +108,7 @@ def get_node_id(node_): 'data': {'source': id_n1, 'target': id_n2}, 'classes': ['is-input']} - if not hide_unconnected_nodes or not derivation_pathway: + if not hide_unconnected_nodes and not derivation_pathway: unconnected_edges = { (node['data']['id'], 'unattached_symbols'): {'data': {'source': node['data']['id'], @@ -233,7 +236,8 @@ def parse_path(pathname, search=None): } -def update_labels(elements, show_models=True, show_symbols=True): +def update_labels(elements, show_models=True, show_symbols=True, + models_to_show=None, symbols_to_show=None): for elem in elements: group = elem['group'] if group == 'edge': @@ -252,8 +256,12 @@ def update_labels(elements, show_models=True, show_symbols=True): continue class_to_add = 'label-off' - if (is_model and show_models) or (is_symbol and show_symbols): - class_to_add = 'label-on' + if is_model and show_models: + if not models_to_show or (get(elem, 'data.id', '').split('model_', 1)[1] in models_to_show): + class_to_add = 'label-on' + elif is_symbol and show_symbols: + if not symbols_to_show or (get(elem, 'data.id', '').split('symbol_', 1)[1] in symbols_to_show): + class_to_add = 'label-on' for val in ('label-on', 'label-off'): try: