Commit 9b2ef564 authored by Jim Hoekstra's avatar Jim Hoekstra 👋🏻
Browse files

app had to be restructured in order to not use the graph object as a global...

app had to be restructured in order to not use the graph object as a global variable, I think this was causing the bug
parent 928f5332
import os
import json
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.exceptions import PreventUpdate
from dash.dependencies import Input, Output, State
from dash_app.graph import Graph
from dash_app.words import AssociatedWords
external_stylesheets = [
......@@ -18,9 +21,12 @@ external_stylesheets = [
app = dash.Dash(name=__name__, external_stylesheets=external_stylesheets, url_base_pathname='/msx/',
suppress_callback_exceptions=True)
graph = Graph()
graph.fill_with_associations('fruit')
cyto_graph = graph.get_graph('msx-graph')
server = app.server
server.config['SECRET_KEY'] = os.environ['MSX_SECRET_KEY']
word2vec_model = AssociatedWords()
app.layout = html.Div(children=[
......@@ -40,7 +46,13 @@ app.layout = html.Div(children=[
])
]),
html.Br(),
html.Div(id='msx-graph-div', children=[cyto_graph]),
html.Div(id='base-word-div',
style={'display': 'none'},
),
html.Div(id='graph-elements-div',
style={'display': 'none'},
),
html.Div(id='msx-graph-div', children=[]),
html.Div(children=[
html.Div(className='row', children=[
html.Div(className='col-3', children=[
......@@ -55,41 +67,94 @@ app.layout = html.Div(children=[
html.Button(id='remove-word-button', n_clicks_timestamp=0, children='Remove Selected Association', className='btn btn-danger btn-lg')]),
]),
]),
]),
])
server = app.server
server.config['SECRET_KEY'] = os.environ['MSX_SECRET_KEY']
@app.callback(
Output(component_id='msx-graph-div', component_property='children'),
Input(component_id='submit-word-button', component_property='n_clicks'),
)
def update_graph_div(submit_word_button):
graph = Graph()
graph.set_nodes_and_edges({'nodes': [], 'edges': []})
return graph.get_cytoscape_graph('msx-graph')
@app.callback(
Output(component_id='msx-graph', component_property='autoRefreshLayout'),
Output(component_id='msx-graph', component_property='elements'),
Input(component_id='add-word-button', component_property='n_clicks_timestamp'),
Input(component_id='remove-word-button', component_property='n_clicks_timestamp'),
Input(component_id='submit-word-button', component_property='n_clicks_timestamp'),
State(component_id='msx-graph', component_property='tapNodeData'),
State(component_id='add-word-input', component_property='value'),
Output(component_id='base-word-div', component_property='children'),
Input(component_id='submit-word-button', component_property='n_clicks'),
State(component_id='base-word-input', component_property='value'),
)
def update_base_word(submit_word_button, base_word_input):
return base_word_input
@app.callback(
Output(component_id='graph-elements-div', component_property='children'),
Input(component_id='submit-word-button', component_property='n_clicks'),
Input(component_id='add-word-button', component_property='n_clicks'),
Input(component_id='remove-word-button', component_property='n_clicks'),
State(component_id='base-word-input', component_property='value'),
State(component_id='add-word-input', component_property='value'),
State(component_id='graph-elements-div', component_property='children'),
State(component_id='msx-graph', component_property='tapNodeData'),
State(component_id='base-word-div', component_property='children'),
prevent_initial_call=True
)
def update_graph_elements(submit_word_button, add_word_button, remove_word_button, base_word_input, add_word_input,
nodes_and_edges, selected_nodes, base_word_state):
callback_context = dash.callback_context
button_id = callback_context.triggered[0]['prop_id'].split('.')[0]
graph = Graph()
if button_id == 'submit-word-button':
graph.fill_with_associations(word2vec_model, base_word_input)
new_nodes_and_edges = graph.get_nodes_and_edges()
return json.dumps(new_nodes_and_edges)
if button_id == 'add-word-button':
graph.set_nodes_and_edges(json.loads(nodes_and_edges))
if add_word_input is not None and add_word_input != '' and add_word_input not in graph.get_all_words():
graph.add_node(add_word_input)
graph.add_edge(base_word_state, add_word_input)
new_nodes_and_edges = json.dumps(graph.get_nodes_and_edges())
return new_nodes_and_edges
else:
raise PreventUpdate
if button_id == 'remove-word-button':
graph.set_nodes_and_edges(json.loads(nodes_and_edges))
if selected_nodes is not None:
selected_word = selected_nodes['label']
if selected_word in graph.get_all_words() and selected_word != base_word_state:
graph.remove_node(selected_nodes['label'])
new_nodes_and_edges = json.dumps(graph.get_nodes_and_edges())
return new_nodes_and_edges
else:
raise PreventUpdate
else:
raise PreventUpdate
@app.callback(
Output(component_id='msx-graph', component_property='elements'),
Input(component_id='graph-elements-div', component_property='children'),
prevent_initial_call=True
)
def update_graph(nodes_and_edges):
graph = Graph()
graph.set_nodes_and_edges(json.loads(nodes_and_edges))
return graph.get_elements()
@app.callback(
Output(component_id='msx-graph', component_property='autoRefreshLayout'),
Input(component_id='add-word-button', component_property='n_clicks'),
Input(component_id='remove-word-button', component_property='n_clicks'),
prevent_initial_call=True
)
def graph_elements_callback(add_button_ts, remove_button_ts, submit_button_ts, graph_selected, add_word, base_word):
if submit_button_ts > remove_button_ts and submit_button_ts > add_button_ts:
graph.fill_with_associations(base_word)
graph_elements = graph.get_graph_elements()
return True, graph_elements
if remove_button_ts > add_button_ts:
if graph_selected is not None:
selected_word = graph_selected['label']
if selected_word in graph.get_all_words() and selected_word != graph.get_base_word():
graph.remove_node(graph_selected['label'])
if add_button_ts > remove_button_ts:
if add_word is not None and add_word != '' and add_word not in graph.get_all_words():
graph.add_node(add_word)
graph.add_edge(graph.get_base_word(), add_word)
graph_elements = graph.get_graph_elements()
return False, graph_elements
def set_auto_refresh_layout(add_word_button, remove_word_button):
return False
import dash_cytoscape as cyto
from dash_app.words import AssociatedWords
class Graph:
def __init__(self):
self.associated_words = AssociatedWords()
self.nodes = []
self.edges = []
def get_base_word(self):
return self.associated_words.get_base_word()
def get_all_words(self):
all_words = [node_dict['data']['label'] for node_dict in self.nodes]
return all_words
......@@ -20,13 +15,8 @@ class Graph:
self.nodes = []
self.edges = []
def fill_with_associations(self, base_word):
self.associated_words.set_base_word(base_word)
self.fill_graph()
def fill_graph(self):
base_word = self.associated_words.get_base_word()
associated_words = self.associated_words.get_associated_words()
def fill_with_associations(self, word2vec_model, base_word):
associated_words = word2vec_model.get_associated_words(base_word)
self.clear_graph_elements()
self.add_node(base_word, is_base_node=1)
self.add_nodes(associated_words)
......@@ -71,14 +61,21 @@ class Graph:
for edge_idx_to_remove in edges_idxs_to_remove:
self.edges.pop(edge_idx_to_remove)
def get_graph_elements(self):
nodes_and_edges = []
nodes_and_edges.extend(self.nodes)
nodes_and_edges.extend(self.edges)
return nodes_and_edges
def get_nodes_and_edges(self):
return {'nodes': self.nodes, 'edges': self.edges}
def set_nodes_and_edges(self, nodes_and_edges):
self.nodes = nodes_and_edges['nodes']
self.edges = nodes_and_edges['edges']
def get_elements(self):
elements = []
elements.extend(self.nodes)
elements.extend(self.edges)
return elements
def get_graph(self, component_id):
elements = self.get_graph_elements()
def get_cytoscape_graph(self, component_id):
elements = self.get_elements()
return cyto.Cytoscape(id=component_id,
autoRefreshLayout=True,
layout={'name': 'cose', 'animate': True},
......
......@@ -10,32 +10,23 @@ class AssociatedWords:
self.model = api.load('glove-twitter-200')
print(" Word2Vec model is ready. Enjoy!!!\n")
self.base_word = None
self.gensim_result = None
self.filtered_results = None
self.words = None
def set_base_word(self, word):
self.base_word = word
self.gensim_result = self.model.most_similar(self.base_word, topn=self.N_RESULTS)
# self.gensim_result = [('apple', 1.0), ('banana', 1.0), ('strawberry', 1.0)]
self.filter_results()
def filter_results(self):
self.filtered_results = [result_tuple for result_tuple in self.gensim_result if self.passes_filter(result_tuple[0])]
self.words = [result_tuple[0] for result_tuple in self.filtered_results]
def passes_filter(self, word):
def get_associated_words(self, word):
gensim_result = self.model.most_similar(word, topn=self.N_RESULTS)
# gensim_result = [('apple', 1.0), ('banana', 1.0), ('strawberry', 1.0)]
words = self.filter_results(gensim_result, word)
return words
def filter_results(self, gensim_result, base_word):
filtered_results = [result_tuple for result_tuple in gensim_result if self.passes_filter(result_tuple[0], base_word)]
words = [result_tuple[0] for result_tuple in filtered_results]
return words
@staticmethod
def passes_filter(word, base_word):
if 'www' in word or \
word in self.base_word or \
sdi.rdlevenshtein_norm(word, self.base_word) < 0.5:
word in base_word or \
sdi.rdlevenshtein_norm(word, base_word) < 0.5:
return False
else:
return True
def get_base_word(self):
return self.base_word
def get_associated_words(self):
return self.words
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment