Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
FoodInformatics
msx-tool
Commits
155eb9ca
Commit
155eb9ca
authored
Mar 31, 2021
by
Jim Hoekstra
👋🏻
Browse files
Merge branch 'master' of
https://git.wur.nl/FoodInformatics/msx-tool
parents
ef35df09
0c49510d
Changes
7
Hide whitespace changes
Inline
Side-by-side
dash_app/app.py
View file @
155eb9ca
import
os
import
dash
import
dash_core_components
as
dcc
import
dash_html_components
as
html
from
dash.dependencies
import
Input
,
Output
,
State
from
dash_app.graph
import
Graph
external_stylesheets
=
[
{
'href'
:
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css'
,
'rel'
:
'stylesheet'
,
'integrity'
:
'sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl'
,
'crossorigin'
:
'anonymous'
}
]
from
dash_app.layout
import
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'
)
app
.
layout
=
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'container'
,
children
=
[
html
.
Br
(),
html
.
Br
(),
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'row'
,
children
=
[
html
.
Div
(
className
=
'col-1'
,
children
=
[
html
.
H2
(
children
=
'Term:'
),
]),
html
.
Div
(
className
=
'col-9'
,
children
=
[
dcc
.
Input
(
id
=
'base-word-input'
,
value
=
''
,
type
=
'text'
,
className
=
'form-control form-control-lg'
),
]),
html
.
Div
(
className
=
'col-2'
,
children
=
[
html
.
Button
(
id
=
'submit-word-button'
,
n_clicks_timestamp
=
0
,
children
=
'Submit Term'
,
className
=
'btn btn-success btn-lg'
)]),
])
]),
html
.
Br
(),
html
.
Div
(
id
=
'msx-graph-div'
,
children
=
[
cyto_graph
]),
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'row'
,
children
=
[
html
.
Div
(
className
=
'col-3'
,
children
=
[
dcc
.
Input
(
id
=
'add-word-input'
,
value
=
''
,
type
=
'text'
,
className
=
'form-control form-control-lg'
),
]),
html
.
Div
(
className
=
'col-2'
,
children
=
[
html
.
Button
(
id
=
'add-word-button'
,
n_clicks_timestamp
=
0
,
children
=
'Add Association'
,
className
=
'btn btn-success btn-lg'
),
]),
html
.
Div
(
className
=
'col-4'
,
children
=
[
]),
html
.
Div
(
className
=
'col-3'
,
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'
,
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'
),
State
(
component_id
=
'base-word-input'
,
component_property
=
'value'
),
)
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
dash_app/callbacks.py
0 → 100644
View file @
155eb9ca
import
dash
from
dash.exceptions
import
PreventUpdate
from
dash.dependencies
import
Input
,
Output
,
State
import
json
from
dash_app.app
import
app
from
dash_app.graph
import
Graph
from
dash_app.words
import
AssociatedWords
word2vec_model
=
AssociatedWords
()
@
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
=
'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
set_auto_refresh_layout
(
add_word_button
,
remove_word_button
):
return
False
dash_app/graph.py
View file @
155eb9ca
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
},
...
...
dash_app/index.py
0 → 100644
View file @
155eb9ca
import
os
from
dash_app.app
import
app
from
dash_app.layout
import
layout
import
dash_app.callbacks
app
.
layout
=
layout
server
=
app
.
server
server
.
config
[
'SECRET_KEY'
]
=
os
.
environ
[
'MSX_SECRET_KEY'
]
dash_app/layout.py
0 → 100644
View file @
155eb9ca
import
dash_core_components
as
dcc
import
dash_html_components
as
html
external_stylesheets
=
[
{
'href'
:
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css'
,
'rel'
:
'stylesheet'
,
'integrity'
:
'sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl'
,
'crossorigin'
:
'anonymous'
}
]
layout
=
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'container'
,
children
=
[
html
.
Br
(),
html
.
Br
(),
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'row'
,
children
=
[
html
.
Div
(
className
=
'col-1'
,
children
=
[
html
.
H2
(
children
=
'Term:'
),
]),
html
.
Div
(
className
=
'col-9'
,
children
=
[
dcc
.
Input
(
id
=
'base-word-input'
,
value
=
'fruit'
,
type
=
'text'
,
className
=
'form-control form-control-lg'
),
]),
html
.
Div
(
className
=
'col-2'
,
children
=
[
html
.
Button
(
id
=
'submit-word-button'
,
n_clicks_timestamp
=
0
,
children
=
'Submit Term'
,
className
=
'btn btn-success btn-lg'
)]),
])
]),
html
.
Br
(),
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
=
[
dcc
.
Input
(
id
=
'add-word-input'
,
value
=
''
,
type
=
'text'
,
className
=
'form-control form-control-lg'
),
]),
html
.
Div
(
className
=
'col-2'
,
children
=
[
html
.
Button
(
id
=
'add-word-button'
,
n_clicks_timestamp
=
0
,
children
=
'Add Association'
,
className
=
'btn btn-success btn-lg'
),
]),
html
.
Div
(
className
=
'col-4'
,
children
=
[
]),
html
.
Div
(
className
=
'col-3'
,
children
=
[
html
.
Button
(
id
=
'remove-word-button'
,
n_clicks_timestamp
=
0
,
children
=
'Remove Selected Association'
,
className
=
'btn btn-danger btn-lg'
)]),
]),
]),
]),
])
dash_app/words.py
View file @
155eb9ca
...
...
@@ -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
scripts/run_debug_server.py
View file @
155eb9ca
from
dash_app.
app
import
app
from
dash_app.
index
import
app
if
__name__
==
'__main__'
:
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment