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/ 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 30bdc92f..5efeb425 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 @@ -10,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, @@ -25,13 +84,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])]) @@ -42,12 +107,30 @@ 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 - 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/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: diff --git a/requirements.txt b/requirements.txt index ec3258d6..8115d33d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +-e git+https://github.com/clegaspi/dash-cytoscape.git#egg=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 .