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
636ad586
Commit
636ad586
authored
Oct 12, 2021
by
Jim Hoekstra
👋🏻
Browse files
Merge branch 'develop' into 'master'
Develop See merge request
!21
parents
1336f30f
8015d9b2
Changes
5
Hide whitespace changes
Inline
Side-by-side
dash_app/app.py
View file @
636ad586
...
...
@@ -8,4 +8,4 @@ if os.getenv('MSX_URL_PREFIX', False):
URL_PREFIX
=
os
.
environ
[
'MSX_URL_PREFIX'
]
app
=
dash
.
Dash
(
name
=
__name__
,
external_stylesheets
=
external_stylesheets
,
url_base_pathname
=
URL_PREFIX
,
suppress_callback_exceptions
=
True
)
suppress_callback_exceptions
=
True
,
title
=
'TALK Tool'
)
dash_app/assets/style.css
0 → 100644
View file @
636ad586
body
{
background-color
:
whitesmoke
;
}
.header
{
height
:
7vh
;
margin-top
:
3vh
;
}
.graph
{
height
:
80vh
;
}
.footer
{
height
:
7vh
;
margin-bottom
:
3vh
;
}
.content
{
width
:
90%
;
margin
:
auto
;
}
dash_app/callbacks.py
View file @
636ad586
...
...
@@ -11,13 +11,33 @@ from dash_app.words import AssociatedWords
word2vec_model
=
AssociatedWords
()
@
app
.
callback
(
Output
(
component_id
=
'msx-graph'
,
component_property
=
'autoRefreshLayout'
),
Input
(
component_id
=
'submit-word-button'
,
component_property
=
'n_clicks'
),
Input
(
component_id
=
'add-word-button'
,
component_property
=
'n_clicks'
),
Input
(
component_id
=
'extend-graph-button'
,
component_property
=
'n_clicks'
),
Input
(
component_id
=
'remove-word-button'
,
component_property
=
'n_clicks'
),
State
(
component_id
=
'msx-graph-div'
,
component_property
=
'children'
),
)
def
set_auto_refresh_layout
(
submit_word_button
,
add_word_button
,
extend_graph_button
,
remove_word_button
,
msx_graph
):
callback_context
=
dash
.
callback_context
button_id
=
callback_context
.
triggered
[
0
][
'prop_id'
].
split
(
'.'
)[
0
]
if
button_id
==
'submit-word-button'
:
return
True
else
:
return
False
@
app
.
callback
(
Output
(
component_id
=
'msx-graph-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_graph_div
(
submit_word_button
):
def
update_graph_div
(
submit_word_button
,
base_word_input
):
graph
=
Graph
()
graph
.
set_nodes_and_edges
({
'nodes'
:
[],
'edges'
:
[]})
base_word_lower
=
base_word_input
.
lower
()
graph
.
add_node
(
base_word_lower
,
is_base_node
=
1
)
# graph.set_nodes_and_edges({'nodes': [], 'edges': []})
return
graph
.
get_cytoscape_graph
(
'msx-graph'
)
...
...
@@ -27,42 +47,57 @@ def update_graph_div(submit_word_button):
State
(
component_id
=
'base-word-input'
,
component_property
=
'value'
),
)
def
update_base_word
(
submit_word_button
,
base_word_input
):
return
base_word_input
if
base_word_input
is
not
None
:
base_word_input_lowercase
=
base_word_input
.
lower
()
if
base_word_input_lowercase
!=
''
:
return
base_word_input_lowercase
else
:
raise
PreventUpdate
else
:
raise
PreventUpdate
@
app
.
callback
(
Output
(
component_id
=
'graph-elements-div'
,
component_property
=
'children'
),
Input
(
component_id
=
'
submit-word-button
'
,
component_property
=
'
n_clicks
'
),
Input
(
component_id
=
'
base-word-div
'
,
component_property
=
'
children
'
),
Input
(
component_id
=
'add-word-button'
,
component_property
=
'n_clicks'
),
Input
(
component_id
=
'extend-graph-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-div'
,
component_property
=
'children'
),
State
(
component_id
=
'msx-graph'
,
component_property
=
'selectedNodeData'
),
State
(
component_id
=
'base-word-div'
,
component_property
=
'children'
),
prevent_initial_call
=
True
)
def
update_graph_elements
(
submit_word_button
,
add_word_button
,
extend_graph_button
,
remove_word_button
,
base_word_input
,
add_word_input
,
nodes_and_edges
,
selected_nodes
,
base_word_state
):
def
update_graph_elements
(
base_word_state
,
add_word_button
,
extend_graph_button
,
remove_word_button
,
base_word_input
,
add_word_input
,
nodes_and_edges
,
msx_graph
,
selected_nodes
):
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
==
'base-word-div'
:
if
base_word_input
is
not
None
:
if
base_word_input
!=
''
:
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
)
else
:
raise
PreventUpdate
raise
PreventUpdate
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
if
add_word_input
is
not
None
:
add_word_input_lowercase
=
add_word_input
.
lower
()
if
add_word_input_lowercase
!=
''
and
add_word_input_lowercase
not
in
graph
.
get_all_words
():
graph
.
add_node
(
add_word_input_lowercase
)
graph
.
add_edge
(
base_word_state
,
add_word_input_lowercase
)
new_nodes_and_edges
=
json
.
dumps
(
graph
.
get_nodes_and_edges
())
return
new_nodes_and_edges
else
:
raise
PreventUpdate
else
:
raise
PreventUpdate
...
...
@@ -91,25 +126,8 @@ def update_graph_elements(submit_word_button, add_word_button, extend_graph_butt
@
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
=
'extend-graph-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
,
extend_graph_button
,
remove_word_button
):
callback_context
=
dash
.
callback_context
button_id
=
callback_context
.
triggered
[
0
][
'prop_id'
].
split
(
'.'
)[
0
]
if
button_id
==
'add-word-button'
or
button_id
==
'remove-word-button'
:
return
False
if
button_id
==
'extend-graph-button'
:
return
True
dash_app/graph.py
View file @
636ad586
import
dash_cytoscape
as
cyto
import
pandas
as
pd
import
numpy
as
np
from
math
import
factorial
...
...
@@ -8,6 +9,7 @@ class Graph:
def
__init__
(
self
):
self
.
nodes
=
[]
self
.
edges
=
[]
self
.
positions
=
{}
self
.
COUNT_THRESHOLD
=
2
self
.
MAX_NUM_WORDS
=
10
...
...
@@ -22,24 +24,44 @@ class Graph:
self
.
edges
=
[]
def
fill_with_associations
(
self
,
word2vec_model
,
base_word
):
associated_words
=
word2vec_model
.
get_associated_words
(
base_word
)
base_word_lower
=
base_word
.
lower
()
associated_words
=
word2vec_model
.
get_associated_words
(
base_word_lower
)
self
.
clear_graph_elements
()
self
.
add_node
(
base_word
,
is_base_node
=
1
)
self
.
add_node
(
base_word
_lower
,
is_base_node
=
1
)
self
.
add_nodes
(
associated_words
)
self
.
add_edges
(
base_word
,
associated_words
)
self
.
add_edges
(
base_word
_lower
,
associated_words
)
def
add_nodes
(
self
,
nodes
):
for
node
in
nodes
:
self
.
add_node
(
node
)
def
add_node
(
self
,
node
,
is_base_node
=
0
):
node_dict
=
{
'data'
:
{
'id'
:
node
,
'label'
:
node
,
'is_base_node'
:
is_base_node
}}
node_dict
=
{
'data'
:
{
'id'
:
node
,
'label'
:
node
,
'is_base_node'
:
is_base_node
}
,
'position'
:
self
.
get_node_position
(
node
,
is_base_node
)
}
if
is_base_node
:
node_dict
[
'selectable'
]
=
False
node_dict
[
'grabbable'
]
=
False
if
node_dict
not
in
self
.
nodes
:
self
.
nodes
.
append
(
node_dict
)
def
get_node_position
(
self
,
node_id
,
is_base_node
):
if
is_base_node
:
return
{
'x'
:
0
,
'y'
:
0
}
else
:
position_id
=
0
while
str
(
position_id
)
in
self
.
positions
:
position_id
+=
1
length
=
position_id
*
4
+
80
angle
=
((
90.0
-
37.5
*
position_id
)
/
180.0
)
*
np
.
pi
if
angle
<
-
np
.
pi
:
angle
+=
(
2
*
np
.
pi
)
x
=
length
*
np
.
cos
(
angle
)
y
=
length
*
np
.
sin
(
angle
)
position
=
{
'x'
:
x
,
'y'
:
-
y
}
self
.
positions
[
str
(
position_id
)]
=
node_id
return
position
def
add_edges
(
self
,
source
,
targets
):
for
target
in
targets
:
self
.
add_edge
(
source
,
target
)
...
...
@@ -56,6 +78,7 @@ class Graph:
if
node_idx_to_remove
>=
0
:
self
.
nodes
.
pop
(
node_idx_to_remove
)
self
.
positions
=
{
position_id
:
node_id
for
position_id
,
node_id
in
self
.
positions
.
items
()
if
node_id
!=
node
}
self
.
remove_edges
(
node
)
def
remove_edges
(
self
,
node
):
...
...
@@ -68,11 +91,12 @@ class Graph:
self
.
edges
.
pop
(
edge_idx_to_remove
)
def
get_nodes_and_edges
(
self
):
return
{
'nodes'
:
self
.
nodes
,
'edges'
:
self
.
edges
}
return
{
'nodes'
:
self
.
nodes
,
'edges'
:
self
.
edges
,
'positions'
:
self
.
positions
}
def
set_nodes_and_edges
(
self
,
nodes_and_edges
):
self
.
nodes
=
nodes_and_edges
[
'nodes'
]
self
.
edges
=
nodes_and_edges
[
'edges'
]
self
.
positions
=
nodes_and_edges
[
'positions'
]
def
get_elements
(
self
):
elements
=
[]
...
...
@@ -83,9 +107,9 @@ class Graph:
def
get_cytoscape_graph
(
self
,
component_id
):
elements
=
self
.
get_elements
()
return
cyto
.
Cytoscape
(
id
=
component_id
,
autoRefreshLayout
=
Tru
e
,
layout
=
{
'name'
:
'
cose'
,
'animate'
:
True
},
style
=
{
'width'
:
'100%'
,
'height'
:
'
550px
'
},
autoRefreshLayout
=
Fals
e
,
layout
=
{
'name'
:
'
preset'
},
style
=
{
'width'
:
'100%'
,
'height'
:
'
100%
'
},
elements
=
elements
,
userZoomingEnabled
=
False
,
userPanningEnabled
=
False
,
...
...
dash_app/layout.py
View file @
636ad586
...
...
@@ -13,11 +13,9 @@ external_stylesheets = [
layout
=
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'container'
,
children
=
[
html
.
Br
(),
html
.
Br
(),
html
.
Div
(
className
=
'content'
,
children
=
[
html
.
Div
(
children
=
[
html
.
Div
(
className
=
'row'
,
children
=
[
html
.
Div
(
className
=
'row
header
'
,
children
=
[
html
.
Div
(
className
=
'col-1'
,
children
=
[
html
.
H2
(
children
=
'Term:'
),
]),
...
...
@@ -28,30 +26,27 @@ layout = html.Div(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-2'
,
children
=
[
html
.
Button
(
id
=
'extend-graph-button'
,
n_clicks_timestamp
=
0
,
children
=
'Extend Graph'
,
className
=
'btn btn-success btn-lg'
),
]),
html
.
Div
(
className
=
'col-2'
,
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'
)]),
]),
html
.
Div
(
className
=
'graph'
,
id
=
'msx-graph-div'
,
children
=
[]),
html
.
Div
(
className
=
'row footer'
,
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-2'
,
children
=
[
html
.
Button
(
id
=
'extend-graph-button'
,
n_clicks_timestamp
=
0
,
children
=
'Extend Graph'
,
className
=
'btn btn-success btn-lg'
),
]),
html
.
Div
(
className
=
'col-2'
,
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'
)]),
]),
]),
])
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