From ba9f8f3ce1f192f8755b12e9e245a569b9e76974 Mon Sep 17 00:00:00 2001
From: Sebastian David <sebastian.david@uni-hamburg.de>
Date: Fri, 12 Nov 2021 11:44:18 +0100
Subject: [PATCH] Code cleanup, added documentation, added start button, added
 recursion depth in both directions, added a basic example of an output
 function, fixed a bug where elements that where deleted and readded would
 still be selected

---
 ui_programm_fragmente/input_to_checklist.py | 104 ++++++++++++++++----
 1 file changed, 86 insertions(+), 18 deletions(-)

diff --git a/ui_programm_fragmente/input_to_checklist.py b/ui_programm_fragmente/input_to_checklist.py
index 0f0ec61..740b51e 100644
--- a/ui_programm_fragmente/input_to_checklist.py
+++ b/ui_programm_fragmente/input_to_checklist.py
@@ -4,49 +4,82 @@ from dash import html
 from dash import callback_context
 from dash.dependencies import Input, Output, State
 from dash.exceptions import PreventUpdate
+import plotly.express as px
 
 app = dash.Dash(__name__)
 
 list_of_inputs = dict()
 
 app.layout = html.Div([
-    html.H4("Progressively add input strings to a list"),
+    # Layer 0: For the Header and Help Function(s)
     html.Div([
         html.Button(id='show-info',children='Show Info',n_clicks=0),
         html.Div(id='info-box')
     ]),
+    # Layer 1: For all mandatory Inputs
     html.Div([
         "Input: ",
-        dcc.Input(id='my-input', value='', type='text',debounce=True),
-        dcc.Checklist(id='list-of-inputs',labelStyle = dict(display='block')),
+        dcc.Input(id='input-string', value='', type='text',debounce=True),
+        dcc.Input(id='forward-depth',value='1',type='number',min='1',max='10'),
+        dcc.Input(id='backward-depth',value='1',type='number',min='1',max='10')
+    ]),
+    # Layer 2: For the checklist and Remove-/Start-Buttons
+    html.Div([
+        dcc.Checklist(id='input-checklist',labelStyle = dict(display='block'),value=[]),
         html.Button(id='clear-all-button',children='Clear All'),
-        html.Button(id='clear-selected-button',children='Clear Selected')
+        html.Button(id='clear-selected-button',children='Clear Selected'),
+        html.Button(id='start-button',children='Generate Graph')
     ]),
+    # Layer 3: For additional Options (e.g. Topological Sort)
+    # Layer 4: For the Graph
+    html.Div([
+        html.Div(id='test-output')
+    ])
 ])
+
+'''
+Most important callback function. Updates the checklist that holds all inputs.
+input-string is required as Output to clear the input box after each input
+'''
 @app.callback(
-    Output('list-of-inputs','options'),
-    Output('my-input','value'),
-    Input('my-input','value'),
+    Output('input-checklist','options'),
+    Output('input-checklist','value'),
+    Output('input-string','value'),
+    Input('input-string','value'),
     Input('clear-all-button','n_clicks'),
     Input('clear-selected-button','n_clicks'),
-    State('list-of-inputs','value')
+    State('input-checklist','value')
 )
-def update_input_list(input_value,btn1,btn2,all_inputs):
+def update_input_list(input_value,btn1,btn2,all_values):
+    '''
+        :param input_value: given by dcc.Input
+        :type input_value: string
+        :param btn1: signals pressing of clear-all-button
+        :param btn2: signals pressing of clear-selected-button
+        :param all_values: values of all checked elements
+        :type all_values: list of strings
+    '''
     changed_id = [p['prop_id'] for p in callback_context.triggered][0]
+    # if clear-all-button was pressed:
     if 'clear-all-button' in changed_id:
         list_of_inputs.clear()
-        return list(),''
+        return list(),list(),''
+    # if clear-selected-button was pressed:
     if 'clear-selected-button' in changed_id:
-        for key in range(len(all_inputs)):
-            if all_inputs[key]:
-                del list_of_inputs[all_inputs[key]]
-        return [{'label': i, 'value': i} for i in list_of_inputs], ''
+        for value in all_values:
+            del list_of_inputs[value]
+        return [{'label': i, 'value': i} for i in list_of_inputs],list(),''
+    # when the programm is first started:
     if input_value == '':
-        return list(),''
+        return list(),list(),''
+    # when a new element is added via dcc.Input
     if input_value not in list_of_inputs:
         list_of_inputs[input_value] = input_value
-    return [{'label': i, 'value': i} for i in list_of_inputs], ''
+    return [{'label': i, 'value': i} for i in list_of_inputs],all_values,''
 
+'''
+This callback shows and hides the (first) help-box
+'''
 @app.callback(
     Output('info-box','children'),
     Input('show-info','n_clicks')
@@ -55,9 +88,44 @@ def show_hide_info_box(n_clicks):
     if n_clicks % 2 == 0:
         return ''
     else:
-        return "Hier koennte Ihre Werbung stehen"
-
+        return 'Hier koennte Ihre Werbung stehen'
 
+'''
+Basic structure for a callback that generates an output
+'''
+@app.callback(
+    Output('test-output','children'),
+    Input('start-button','n_clicks'),
+    State('input-checklist','options'),
+    State('input-checklist','value'),
+    State('forward-depth','value'),
+    State('backward-depth','value'),
+)
+def generate_output(n_clicks,all_options,all_values,forward_depth,backward_depth):
+    '''
+        :param n_clicks: how often has Generate Graph been clicked
+        :type n_clicks: int
+        :param all_options: all labels and values from the checklist,
+            regardless if they have been checked or not
+        :type all_options: list of dictionaries with 2 entries each
+        :param all_values: values of all checked elements
+        :type all_values: list of strings
+        :param forward_depth: forward recursion depth
+        :type forward_depth: unsigned int
+        :param backward_depth: backward recursion depth
+        :type backward_depth: unsigned int
+    '''
+    if n_clicks is None:
+        raise PreventUpdate
+    else:
+        s = ''
+        for i in range(len(all_options)):
+            x = all_options[i]['value']
+            if x in all_values:
+                s += x*(abs(int(forward_depth)-int(backward_depth)))
+            else:
+                s += x*(int(forward_depth)+int(backward_depth))
+        return s
 
 if __name__ == '__main__':
     app.run_server(debug=True)
-- 
GitLab