We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Tasks per app graph appears as a sawtooth, not as rectangles
See attached plot.

This looks like it plots the number of data points at the point a task starts, and then the next point after a task ends, with linear interpolation between the two points. This is an incorrect visualisation: a task does not fade from existing to not existing over the entire duration of execution; instead it exists at full strength for the full duration of existence, and should be represented on the graph as a rectangular, not saw tooth, plot.

![newplot](https://user-images.githubusercontent.com/280107/59189640-21b7d200-8b6a-11e9-8c31-484323f550c0.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsl/monitoring/visualization/plots/default/workflow_plots.py`
Content:
```
1 import numpy as np
2 import plotly.graph_objs as go
3 import plotly.figure_factory as ff
4 from plotly.offline import plot
5 import networkx as nx
6 import datetime
7 
8 from parsl.monitoring.visualization.utils import timestamp_to_int, num_to_timestamp, DB_DATE_FORMAT
9 
10 
11 def task_gantt_plot(df_task, time_completed=None):
12 
13     df_task = df_task.sort_values(by=['task_time_submitted'], ascending=False)
14 
15     # df_task['task_time_submitted'] = pd.to_datetime(df_task['task_time_submitted'], unit='s')
16     # df_task['task_time_returned'] = pd.to_datetime(df_task['task_time_returned'], unit='s')
17 
18     # df_task = df_task.rename(index=str, columns={"task_id": "Task",
19     #                                             "task_time_submitted": "Start",
20     #                                             "task_time_returned": "Finish",
21     #                                             })
22     # parsl_tasks = df_task.to_dict('records')
23     parsl_tasks = []
24     for i, task in df_task.iterrows():
25         time_running, time_returned = task['task_time_running'], task['task_time_returned']
26         if task['task_time_returned'] is None:
27             time_returned = datetime.datetime.now()
28             if time_completed is not None:
29                 time_returned = time_completed
30         if task['task_time_running'] is None:
31             time_running = task['task_time_submitted']
32         description = "Task ID: {}, app: {}".format(task['task_id'], task['task_func_name'])
33         dic1 = dict(Task=description, Start=task['task_time_submitted'],
34                     Finish=time_running, Resource="Pending")
35         dic2 = dict(Task=description, Start=time_running,
36                     Finish=time_returned, Resource="Running")
37         parsl_tasks.extend([dic1, dic2])
38     colors = {'Pending': 'rgb(168, 168, 168)', 'Running': 'rgb(0, 0, 255)'}
39     fig = ff.create_gantt(parsl_tasks,
40                           title="",
41                           colors=colors,
42                           group_tasks=True,
43                           show_colorbar=True,
44                           index_col='Resource',
45                           )
46     fig['layout']['yaxis']['title'] = 'Task'
47     fig['layout']['yaxis']['showticklabels'] = False
48     fig['layout']['xaxis']['title'] = 'Time'
49     return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
50 
51 
52 def task_per_app_plot(df_task, df_status):
53 
54     def y_axis_setup(array):
55         count = 0
56         items = []
57         for n in array:
58             if n:
59                 count += 1
60             elif count > 0:
61                 count -= 1
62             items.append(count)
63         return items
64 
65     # Fill up dict "apps" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}
66     apps_dict = dict()
67     for i in range(len(df_task)):
68         row = df_task.iloc[i]
69         if row['task_func_name'] in apps_dict:
70             apps_dict[row['task_func_name']].append(row['task_id'])
71         else:
72             apps_dict[row['task_func_name']] = [row['task_id']]
73 
74     fig = go.Figure(
75         data=[go.Scatter(x=df_status[df_status['task_id'].isin(tasks)]['timestamp'],
76                          y=y_axis_setup(df_status[df_status['task_id'].isin(
77                              tasks)]['task_status_name'] == 'running'),
78                          name=app)
79               for app, tasks in apps_dict.items()] +
80         [go.Scatter(x=df_status['timestamp'],
81                     y=y_axis_setup(
82             df_status['task_status_name'] == 'running'),
83             name='all')],
84         layout=go.Layout(xaxis=dict(tickformat='%m-%d\n%H:%M:%S',
85                                     autorange=True,
86                                     title='Time'),
87                          yaxis=dict(tickformat=',d',
88                                     title='Tasks'),
89                          hovermode='closest',
90                          title='Tasks per app'))
91 
92     return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
93 
94 
95 def total_tasks_plot(df_task, df_status, columns=20):
96 
97     min_time = timestamp_to_int(min(df_status['timestamp']))
98     max_time = timestamp_to_int(max(df_status['timestamp']))
99     time_step = (max_time - min_time) / columns
100 
101     x_axis = []
102     for i in np.arange(min_time, max_time + time_step, time_step):
103         x_axis.append(num_to_timestamp(i).strftime(DB_DATE_FORMAT))
104 
105     # Fill up dict "apps" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}
106     apps_dict = dict()
107     for i in range(len(df_task)):
108         row = df_task.iloc[i]
109         if row['task_func_name'] in apps_dict:
110             apps_dict[row['task_func_name']].append(row['task_id'])
111         else:
112             apps_dict[row['task_func_name']] = [row['task_id']]
113 
114     def y_axis_setup(value):
115         items = []
116         for app, tasks in apps_dict.items():
117             tmp = []
118             task = df_status[df_status['task_id'].isin(tasks)]
119             for i in range(len(x_axis) - 1):
120                 x = task['timestamp'] >= x_axis[i]
121                 y = task['timestamp'] < x_axis[i + 1]
122                 tmp.append(sum(task.loc[x & y]['task_status_name'] == value))
123             items = np.sum([items, tmp], axis=0)
124 
125         return items
126 
127     y_axis_done = y_axis_setup('done')
128     y_axis_failed = y_axis_setup('failed')
129 
130     fig = go.Figure(data=[go.Bar(x=x_axis[:-1],
131                                  y=y_axis_done,
132                                  name='done'),
133                           go.Bar(x=x_axis[:-1],
134                                  y=y_axis_failed,
135                                  name='failed')],
136                     layout=go.Layout(xaxis=dict(tickformat='%m-%d\n%H:%M:%S',
137                                                 autorange=True,
138                                                 title='Time'),
139                                      yaxis=dict(tickformat=',d',
140                                                 title='Running tasks.' ' Bin width: ' + num_to_timestamp(time_step).strftime('%Mm%Ss')),
141                                      annotations=[
142                                          dict(
143                                              x=0,
144                                              y=1.07,
145                                              showarrow=False,
146                                              text='Total Done: ' +
147                                              str(sum(y_axis_done)),
148                                              xref='paper',
149                                              yref='paper'
150                                          ),
151                                          dict(
152                                              x=0,
153                                              y=1.05,
154                                              showarrow=False,
155                                              text='Total Failed: ' +
156                                              str(sum(y_axis_failed)),
157                                              xref='paper',
158                                              yref='paper'
159                                          ),
160                     ],
161         barmode='stack',
162         title="Total tasks"))
163 
164     return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
165 
166 
167 def workflow_dag_plot(df_tasks, group_by_apps=True):
168     G = nx.DiGraph(directed=True)
169     nodes = df_tasks['task_id'].unique()
170     dic = df_tasks.set_index('task_id').to_dict()
171     G.add_nodes_from(nodes)
172 
173     # Add edges or links between the nodes:
174     edges = []
175     for k, v in dic['task_depends'].items():
176         if v:
177             adj = v.split(",")
178             for e in adj:
179                 edges.append((int(e), k))
180     G.add_edges_from(edges)
181 
182     node_positions = nx.nx_pydot.pydot_layout(G, prog='dot')
183     node_traces = []
184 
185     if group_by_apps:
186         groups_list = {app: i for i, app in enumerate(
187             df_tasks['task_func_name'].unique())}
188     else:
189         groups_list = {'Pending': (0, 'gray'), "Running": (1, 'blue'), 'Completed': (2, 'green')}
190 
191     for k, _ in groups_list.items():
192         node_trace = go.Scatter(
193             x=[],
194             y=[],
195             text=[],
196             mode='markers',
197             textposition='top center',
198             textfont=dict(
199                 family='arial',
200                 size=18,
201                 color='rgb(0,0,0)'
202             ),
203             hoverinfo='text',
204             name=k,          # legend app_name here
205             marker=dict(
206                 showscale=False,
207                 # color='rgb(200,0,0)',
208                 size=11,
209                 line=dict(width=1, color='rgb(0,0,0)')))
210         node_traces.append(node_trace)
211 
212     for node in node_positions:
213         x, y = node_positions[node]
214         if group_by_apps:
215             name = dic['task_func_name'][node]
216             index = groups_list[name]
217         else:
218             if dic['task_time_returned'][node] is not None:
219                 name = 'Completed'
220             elif dic['task_time_running'][node] is not None:
221                 name = "Running"
222             elif dic['task_time_submitted'][node] is not None:
223                 name = "Pending"
224             index, color = groups_list[name]
225             node_traces[index]['marker']['color'] = color
226         node_traces[index]['x'] += tuple([x])
227         node_traces[index]['y'] += tuple([y])
228         node_traces[index]['text'] += tuple(
229             ["{}:{}".format(dic['task_func_name'][node], node)])
230 
231     # The edges will be drawn as lines:
232     edge_trace = go.Scatter(
233         x=[],
234         y=[],
235         line=dict(width=1, color='rgb(160,160,160)'),
236         hoverinfo='none',
237         # showlegend=False,
238         name='Dependency',
239         mode='lines')
240 
241     for edge in G.edges:
242         x0, y0 = node_positions[edge[0]]
243         x1, y1 = node_positions[edge[1]]
244         edge_trace['x'] += tuple([x0, x1, None])
245         edge_trace['y'] += tuple([y0, y1, None])
246 
247     # Create figure:
248     fig = go.Figure(data=[edge_trace] + node_traces,
249                     layout=go.Layout(
250                     title='Workflow DAG',
251                     titlefont=dict(size=16),
252                     showlegend=True,
253                     hovermode='closest',
254                     margin=dict(b=20, l=5, r=5, t=40),   # noqa: E741
255                     xaxis=dict(showgrid=False, zeroline=False,
256                                showticklabels=False),
257                     yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)))
258     return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
259
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parsl/monitoring/visualization/plots/default/workflow_plots.py b/parsl/monitoring/visualization/plots/default/workflow_plots.py
--- a/parsl/monitoring/visualization/plots/default/workflow_plots.py
+++ b/parsl/monitoring/visualization/plots/default/workflow_plots.py
@@ -1,4 +1,5 @@
 import numpy as np
+import pandas as pd
 import plotly.graph_objs as go
 import plotly.figure_factory as ff
 from plotly.offline import plot
@@ -49,47 +50,39 @@
     return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
 
 
-def task_per_app_plot(df_task, df_status):
-
-    def y_axis_setup(array):
-        count = 0
-        items = []
-        for n in array:
-            if n:
-                count += 1
-            elif count > 0:
-                count -= 1
-            items.append(count)
-        return items
-
-    # Fill up dict "apps" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}
-    apps_dict = dict()
-    for i in range(len(df_task)):
-        row = df_task.iloc[i]
-        if row['task_func_name'] in apps_dict:
-            apps_dict[row['task_func_name']].append(row['task_id'])
-        else:
-            apps_dict[row['task_func_name']] = [row['task_id']]
-
-    fig = go.Figure(
-        data=[go.Scatter(x=df_status[df_status['task_id'].isin(tasks)]['timestamp'],
-                         y=y_axis_setup(df_status[df_status['task_id'].isin(
-                             tasks)]['task_status_name'] == 'running'),
-                         name=app)
-              for app, tasks in apps_dict.items()] +
-        [go.Scatter(x=df_status['timestamp'],
-                    y=y_axis_setup(
-            df_status['task_status_name'] == 'running'),
-            name='all')],
-        layout=go.Layout(xaxis=dict(tickformat='%m-%d\n%H:%M:%S',
-                                    autorange=True,
-                                    title='Time'),
-                         yaxis=dict(tickformat=',d',
-                                    title='Tasks'),
-                         hovermode='closest',
-                         title='Tasks per app'))
-
-    return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
+def task_per_app_plot(task, status):
+
+    try:
+        task['epoch_time_running'] = (pd.to_datetime(
+            task['task_time_running']) - pd.Timestamp("1970-01-01")) // pd.Timedelta('1s')
+        task['epoch_time_returned'] = (pd.to_datetime(
+            task['task_time_returned']) - pd.Timestamp("1970-01-01")) // pd.Timedelta('1s')
+        start = task['epoch_time_running'].min()
+        end = task['epoch_time_returned'].max()
+        tasks_per_app = {}
+        all_tasks = [0] * (end - start + 1)
+        for i, row in task.iterrows():
+            if row['task_func_name'] not in tasks_per_app:
+                tasks_per_app[row['task_func_name']] = [0] * (end - start + 1)
+            for j in range(int(row['epoch_time_running']) + 1, int(row['epoch_time_returned']) + 1):
+                tasks_per_app[row['task_func_name']][j - start] += 1
+                all_tasks[j - start] += 1
+        fig = go.Figure(
+            data=[go.Scatter(x=list(range(0, end - start + 1)),
+                             y=tasks_per_app[app],
+                             name=app,
+                             ) for app in tasks_per_app] +
+                 [go.Scatter(x=list(range(0, end - start + 1)),
+                             y=all_tasks,
+                             name='All',
+                             )],
+            layout=go.Layout(xaxis=dict(autorange=True,
+                                        title='Time (seconds)'),
+                             yaxis=dict(title='Number of tasks'),
+                             title="Tasks per app"))
+        return plot(fig, show_link=False, output_type="div", include_plotlyjs=False)
+    except Exception as e:
+        return "The tasks per app plot cannot be generated because of exception {}.".format(e)
 
 
 def total_tasks_plot(df_task, df_status, columns=20):

{"golden_diff": "diff --git a/parsl/monitoring/visualization/plots/default/workflow_plots.py b/parsl/monitoring/visualization/plots/default/workflow_plots.py\n--- a/parsl/monitoring/visualization/plots/default/workflow_plots.py\n+++ b/parsl/monitoring/visualization/plots/default/workflow_plots.py\n@@ -1,4 +1,5 @@\n import numpy as np\n+import pandas as pd\n import plotly.graph_objs as go\n import plotly.figure_factory as ff\n from plotly.offline import plot\n@@ -49,47 +50,39 @@\n     return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n \n \n-def task_per_app_plot(df_task, df_status):\n-\n-    def y_axis_setup(array):\n-        count = 0\n-        items = []\n-        for n in array:\n-            if n:\n-                count += 1\n-            elif count > 0:\n-                count -= 1\n-            items.append(count)\n-        return items\n-\n-    # Fill up dict \"apps\" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}\n-    apps_dict = dict()\n-    for i in range(len(df_task)):\n-        row = df_task.iloc[i]\n-        if row['task_func_name'] in apps_dict:\n-            apps_dict[row['task_func_name']].append(row['task_id'])\n-        else:\n-            apps_dict[row['task_func_name']] = [row['task_id']]\n-\n-    fig = go.Figure(\n-        data=[go.Scatter(x=df_status[df_status['task_id'].isin(tasks)]['timestamp'],\n-                         y=y_axis_setup(df_status[df_status['task_id'].isin(\n-                             tasks)]['task_status_name'] == 'running'),\n-                         name=app)\n-              for app, tasks in apps_dict.items()] +\n-        [go.Scatter(x=df_status['timestamp'],\n-                    y=y_axis_setup(\n-            df_status['task_status_name'] == 'running'),\n-            name='all')],\n-        layout=go.Layout(xaxis=dict(tickformat='%m-%d\\n%H:%M:%S',\n-                                    autorange=True,\n-                                    title='Time'),\n-                         yaxis=dict(tickformat=',d',\n-                                    title='Tasks'),\n-                         hovermode='closest',\n-                         title='Tasks per app'))\n-\n-    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n+def task_per_app_plot(task, status):\n+\n+    try:\n+        task['epoch_time_running'] = (pd.to_datetime(\n+            task['task_time_running']) - pd.Timestamp(\"1970-01-01\")) // pd.Timedelta('1s')\n+        task['epoch_time_returned'] = (pd.to_datetime(\n+            task['task_time_returned']) - pd.Timestamp(\"1970-01-01\")) // pd.Timedelta('1s')\n+        start = task['epoch_time_running'].min()\n+        end = task['epoch_time_returned'].max()\n+        tasks_per_app = {}\n+        all_tasks = [0] * (end - start + 1)\n+        for i, row in task.iterrows():\n+            if row['task_func_name'] not in tasks_per_app:\n+                tasks_per_app[row['task_func_name']] = [0] * (end - start + 1)\n+            for j in range(int(row['epoch_time_running']) + 1, int(row['epoch_time_returned']) + 1):\n+                tasks_per_app[row['task_func_name']][j - start] += 1\n+                all_tasks[j - start] += 1\n+        fig = go.Figure(\n+            data=[go.Scatter(x=list(range(0, end - start + 1)),\n+                             y=tasks_per_app[app],\n+                             name=app,\n+                             ) for app in tasks_per_app] +\n+                 [go.Scatter(x=list(range(0, end - start + 1)),\n+                             y=all_tasks,\n+                             name='All',\n+                             )],\n+            layout=go.Layout(xaxis=dict(autorange=True,\n+                                        title='Time (seconds)'),\n+                             yaxis=dict(title='Number of tasks'),\n+                             title=\"Tasks per app\"))\n+        return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n+    except Exception as e:\n+        return \"The tasks per app plot cannot be generated because of exception {}.\".format(e)\n \n \n def total_tasks_plot(df_task, df_status, columns=20):\n", "issue": "Tasks per app graph appears as a sawtooth, not as rectangles\nSee attached plot.\r\n\r\nThis looks like it plots the number of data points at the point a task starts, and then the next point after a task ends, with linear interpolation between the two points. This is an incorrect visualisation: a task does not fade from existing to not existing over the entire duration of execution; instead it exists at full strength for the full duration of existence, and should be represented on the graph as a rectangular, not saw tooth, plot.\r\n\r\n![newplot](https://user-images.githubusercontent.com/280107/59189640-21b7d200-8b6a-11e9-8c31-484323f550c0.png)\r\n\n", "before_files": [{"content": "import numpy as np\nimport plotly.graph_objs as go\nimport plotly.figure_factory as ff\nfrom plotly.offline import plot\nimport networkx as nx\nimport datetime\n\nfrom parsl.monitoring.visualization.utils import timestamp_to_int, num_to_timestamp, DB_DATE_FORMAT\n\n\ndef task_gantt_plot(df_task, time_completed=None):\n\n    df_task = df_task.sort_values(by=['task_time_submitted'], ascending=False)\n\n    # df_task['task_time_submitted'] = pd.to_datetime(df_task['task_time_submitted'], unit='s')\n    # df_task['task_time_returned'] = pd.to_datetime(df_task['task_time_returned'], unit='s')\n\n    # df_task = df_task.rename(index=str, columns={\"task_id\": \"Task\",\n    #                                             \"task_time_submitted\": \"Start\",\n    #                                             \"task_time_returned\": \"Finish\",\n    #                                             })\n    # parsl_tasks = df_task.to_dict('records')\n    parsl_tasks = []\n    for i, task in df_task.iterrows():\n        time_running, time_returned = task['task_time_running'], task['task_time_returned']\n        if task['task_time_returned'] is None:\n            time_returned = datetime.datetime.now()\n            if time_completed is not None:\n                time_returned = time_completed\n        if task['task_time_running'] is None:\n            time_running = task['task_time_submitted']\n        description = \"Task ID: {}, app: {}\".format(task['task_id'], task['task_func_name'])\n        dic1 = dict(Task=description, Start=task['task_time_submitted'],\n                    Finish=time_running, Resource=\"Pending\")\n        dic2 = dict(Task=description, Start=time_running,\n                    Finish=time_returned, Resource=\"Running\")\n        parsl_tasks.extend([dic1, dic2])\n    colors = {'Pending': 'rgb(168, 168, 168)', 'Running': 'rgb(0, 0, 255)'}\n    fig = ff.create_gantt(parsl_tasks,\n                          title=\"\",\n                          colors=colors,\n                          group_tasks=True,\n                          show_colorbar=True,\n                          index_col='Resource',\n                          )\n    fig['layout']['yaxis']['title'] = 'Task'\n    fig['layout']['yaxis']['showticklabels'] = False\n    fig['layout']['xaxis']['title'] = 'Time'\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n\n\ndef task_per_app_plot(df_task, df_status):\n\n    def y_axis_setup(array):\n        count = 0\n        items = []\n        for n in array:\n            if n:\n                count += 1\n            elif count > 0:\n                count -= 1\n            items.append(count)\n        return items\n\n    # Fill up dict \"apps\" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}\n    apps_dict = dict()\n    for i in range(len(df_task)):\n        row = df_task.iloc[i]\n        if row['task_func_name'] in apps_dict:\n            apps_dict[row['task_func_name']].append(row['task_id'])\n        else:\n            apps_dict[row['task_func_name']] = [row['task_id']]\n\n    fig = go.Figure(\n        data=[go.Scatter(x=df_status[df_status['task_id'].isin(tasks)]['timestamp'],\n                         y=y_axis_setup(df_status[df_status['task_id'].isin(\n                             tasks)]['task_status_name'] == 'running'),\n                         name=app)\n              for app, tasks in apps_dict.items()] +\n        [go.Scatter(x=df_status['timestamp'],\n                    y=y_axis_setup(\n            df_status['task_status_name'] == 'running'),\n            name='all')],\n        layout=go.Layout(xaxis=dict(tickformat='%m-%d\\n%H:%M:%S',\n                                    autorange=True,\n                                    title='Time'),\n                         yaxis=dict(tickformat=',d',\n                                    title='Tasks'),\n                         hovermode='closest',\n                         title='Tasks per app'))\n\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n\n\ndef total_tasks_plot(df_task, df_status, columns=20):\n\n    min_time = timestamp_to_int(min(df_status['timestamp']))\n    max_time = timestamp_to_int(max(df_status['timestamp']))\n    time_step = (max_time - min_time) / columns\n\n    x_axis = []\n    for i in np.arange(min_time, max_time + time_step, time_step):\n        x_axis.append(num_to_timestamp(i).strftime(DB_DATE_FORMAT))\n\n    # Fill up dict \"apps\" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}\n    apps_dict = dict()\n    for i in range(len(df_task)):\n        row = df_task.iloc[i]\n        if row['task_func_name'] in apps_dict:\n            apps_dict[row['task_func_name']].append(row['task_id'])\n        else:\n            apps_dict[row['task_func_name']] = [row['task_id']]\n\n    def y_axis_setup(value):\n        items = []\n        for app, tasks in apps_dict.items():\n            tmp = []\n            task = df_status[df_status['task_id'].isin(tasks)]\n            for i in range(len(x_axis) - 1):\n                x = task['timestamp'] >= x_axis[i]\n                y = task['timestamp'] < x_axis[i + 1]\n                tmp.append(sum(task.loc[x & y]['task_status_name'] == value))\n            items = np.sum([items, tmp], axis=0)\n\n        return items\n\n    y_axis_done = y_axis_setup('done')\n    y_axis_failed = y_axis_setup('failed')\n\n    fig = go.Figure(data=[go.Bar(x=x_axis[:-1],\n                                 y=y_axis_done,\n                                 name='done'),\n                          go.Bar(x=x_axis[:-1],\n                                 y=y_axis_failed,\n                                 name='failed')],\n                    layout=go.Layout(xaxis=dict(tickformat='%m-%d\\n%H:%M:%S',\n                                                autorange=True,\n                                                title='Time'),\n                                     yaxis=dict(tickformat=',d',\n                                                title='Running tasks.' ' Bin width: ' + num_to_timestamp(time_step).strftime('%Mm%Ss')),\n                                     annotations=[\n                                         dict(\n                                             x=0,\n                                             y=1.07,\n                                             showarrow=False,\n                                             text='Total Done: ' +\n                                             str(sum(y_axis_done)),\n                                             xref='paper',\n                                             yref='paper'\n                                         ),\n                                         dict(\n                                             x=0,\n                                             y=1.05,\n                                             showarrow=False,\n                                             text='Total Failed: ' +\n                                             str(sum(y_axis_failed)),\n                                             xref='paper',\n                                             yref='paper'\n                                         ),\n                    ],\n        barmode='stack',\n        title=\"Total tasks\"))\n\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n\n\ndef workflow_dag_plot(df_tasks, group_by_apps=True):\n    G = nx.DiGraph(directed=True)\n    nodes = df_tasks['task_id'].unique()\n    dic = df_tasks.set_index('task_id').to_dict()\n    G.add_nodes_from(nodes)\n\n    # Add edges or links between the nodes:\n    edges = []\n    for k, v in dic['task_depends'].items():\n        if v:\n            adj = v.split(\",\")\n            for e in adj:\n                edges.append((int(e), k))\n    G.add_edges_from(edges)\n\n    node_positions = nx.nx_pydot.pydot_layout(G, prog='dot')\n    node_traces = []\n\n    if group_by_apps:\n        groups_list = {app: i for i, app in enumerate(\n            df_tasks['task_func_name'].unique())}\n    else:\n        groups_list = {'Pending': (0, 'gray'), \"Running\": (1, 'blue'), 'Completed': (2, 'green')}\n\n    for k, _ in groups_list.items():\n        node_trace = go.Scatter(\n            x=[],\n            y=[],\n            text=[],\n            mode='markers',\n            textposition='top center',\n            textfont=dict(\n                family='arial',\n                size=18,\n                color='rgb(0,0,0)'\n            ),\n            hoverinfo='text',\n            name=k,          # legend app_name here\n            marker=dict(\n                showscale=False,\n                # color='rgb(200,0,0)',\n                size=11,\n                line=dict(width=1, color='rgb(0,0,0)')))\n        node_traces.append(node_trace)\n\n    for node in node_positions:\n        x, y = node_positions[node]\n        if group_by_apps:\n            name = dic['task_func_name'][node]\n            index = groups_list[name]\n        else:\n            if dic['task_time_returned'][node] is not None:\n                name = 'Completed'\n            elif dic['task_time_running'][node] is not None:\n                name = \"Running\"\n            elif dic['task_time_submitted'][node] is not None:\n                name = \"Pending\"\n            index, color = groups_list[name]\n            node_traces[index]['marker']['color'] = color\n        node_traces[index]['x'] += tuple([x])\n        node_traces[index]['y'] += tuple([y])\n        node_traces[index]['text'] += tuple(\n            [\"{}:{}\".format(dic['task_func_name'][node], node)])\n\n    # The edges will be drawn as lines:\n    edge_trace = go.Scatter(\n        x=[],\n        y=[],\n        line=dict(width=1, color='rgb(160,160,160)'),\n        hoverinfo='none',\n        # showlegend=False,\n        name='Dependency',\n        mode='lines')\n\n    for edge in G.edges:\n        x0, y0 = node_positions[edge[0]]\n        x1, y1 = node_positions[edge[1]]\n        edge_trace['x'] += tuple([x0, x1, None])\n        edge_trace['y'] += tuple([y0, y1, None])\n\n    # Create figure:\n    fig = go.Figure(data=[edge_trace] + node_traces,\n                    layout=go.Layout(\n                    title='Workflow DAG',\n                    titlefont=dict(size=16),\n                    showlegend=True,\n                    hovermode='closest',\n                    margin=dict(b=20, l=5, r=5, t=40),   # noqa: E741\n                    xaxis=dict(showgrid=False, zeroline=False,\n                               showticklabels=False),\n                    yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)))\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n", "path": "parsl/monitoring/visualization/plots/default/workflow_plots.py"}], "after_files": [{"content": "import numpy as np\nimport pandas as pd\nimport plotly.graph_objs as go\nimport plotly.figure_factory as ff\nfrom plotly.offline import plot\nimport networkx as nx\nimport datetime\n\nfrom parsl.monitoring.visualization.utils import timestamp_to_int, num_to_timestamp, DB_DATE_FORMAT\n\n\ndef task_gantt_plot(df_task, time_completed=None):\n\n    df_task = df_task.sort_values(by=['task_time_submitted'], ascending=False)\n\n    # df_task['task_time_submitted'] = pd.to_datetime(df_task['task_time_submitted'], unit='s')\n    # df_task['task_time_returned'] = pd.to_datetime(df_task['task_time_returned'], unit='s')\n\n    # df_task = df_task.rename(index=str, columns={\"task_id\": \"Task\",\n    #                                             \"task_time_submitted\": \"Start\",\n    #                                             \"task_time_returned\": \"Finish\",\n    #                                             })\n    # parsl_tasks = df_task.to_dict('records')\n    parsl_tasks = []\n    for i, task in df_task.iterrows():\n        time_running, time_returned = task['task_time_running'], task['task_time_returned']\n        if task['task_time_returned'] is None:\n            time_returned = datetime.datetime.now()\n            if time_completed is not None:\n                time_returned = time_completed\n        if task['task_time_running'] is None:\n            time_running = task['task_time_submitted']\n        description = \"Task ID: {}, app: {}\".format(task['task_id'], task['task_func_name'])\n        dic1 = dict(Task=description, Start=task['task_time_submitted'],\n                    Finish=time_running, Resource=\"Pending\")\n        dic2 = dict(Task=description, Start=time_running,\n                    Finish=time_returned, Resource=\"Running\")\n        parsl_tasks.extend([dic1, dic2])\n    colors = {'Pending': 'rgb(168, 168, 168)', 'Running': 'rgb(0, 0, 255)'}\n    fig = ff.create_gantt(parsl_tasks,\n                          title=\"\",\n                          colors=colors,\n                          group_tasks=True,\n                          show_colorbar=True,\n                          index_col='Resource',\n                          )\n    fig['layout']['yaxis']['title'] = 'Task'\n    fig['layout']['yaxis']['showticklabels'] = False\n    fig['layout']['xaxis']['title'] = 'Time'\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n\n\ndef task_per_app_plot(task, status):\n\n    try:\n        task['epoch_time_running'] = (pd.to_datetime(\n            task['task_time_running']) - pd.Timestamp(\"1970-01-01\")) // pd.Timedelta('1s')\n        task['epoch_time_returned'] = (pd.to_datetime(\n            task['task_time_returned']) - pd.Timestamp(\"1970-01-01\")) // pd.Timedelta('1s')\n        start = task['epoch_time_running'].min()\n        end = task['epoch_time_returned'].max()\n        tasks_per_app = {}\n        all_tasks = [0] * (end - start + 1)\n        for i, row in task.iterrows():\n            if row['task_func_name'] not in tasks_per_app:\n                tasks_per_app[row['task_func_name']] = [0] * (end - start + 1)\n            for j in range(int(row['epoch_time_running']) + 1, int(row['epoch_time_returned']) + 1):\n                tasks_per_app[row['task_func_name']][j - start] += 1\n                all_tasks[j - start] += 1\n        fig = go.Figure(\n            data=[go.Scatter(x=list(range(0, end - start + 1)),\n                             y=tasks_per_app[app],\n                             name=app,\n                             ) for app in tasks_per_app] +\n                 [go.Scatter(x=list(range(0, end - start + 1)),\n                             y=all_tasks,\n                             name='All',\n                             )],\n            layout=go.Layout(xaxis=dict(autorange=True,\n                                        title='Time (seconds)'),\n                             yaxis=dict(title='Number of tasks'),\n                             title=\"Tasks per app\"))\n        return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n    except Exception as e:\n        return \"The tasks per app plot cannot be generated because of exception {}.\".format(e)\n\n\ndef total_tasks_plot(df_task, df_status, columns=20):\n\n    min_time = timestamp_to_int(min(df_status['timestamp']))\n    max_time = timestamp_to_int(max(df_status['timestamp']))\n    time_step = (max_time - min_time) / columns\n\n    x_axis = []\n    for i in np.arange(min_time, max_time + time_step, time_step):\n        x_axis.append(num_to_timestamp(i).strftime(DB_DATE_FORMAT))\n\n    # Fill up dict \"apps\" like: {app1: [#task1, #task2], app2: [#task4], app3: [#task3]}\n    apps_dict = dict()\n    for i in range(len(df_task)):\n        row = df_task.iloc[i]\n        if row['task_func_name'] in apps_dict:\n            apps_dict[row['task_func_name']].append(row['task_id'])\n        else:\n            apps_dict[row['task_func_name']] = [row['task_id']]\n\n    def y_axis_setup(value):\n        items = []\n        for app, tasks in apps_dict.items():\n            tmp = []\n            task = df_status[df_status['task_id'].isin(tasks)]\n            for i in range(len(x_axis) - 1):\n                x = task['timestamp'] >= x_axis[i]\n                y = task['timestamp'] < x_axis[i + 1]\n                tmp.append(sum(task.loc[x & y]['task_status_name'] == value))\n            items = np.sum([items, tmp], axis=0)\n\n        return items\n\n    y_axis_done = y_axis_setup('done')\n    y_axis_failed = y_axis_setup('failed')\n\n    fig = go.Figure(data=[go.Bar(x=x_axis[:-1],\n                                 y=y_axis_done,\n                                 name='done'),\n                          go.Bar(x=x_axis[:-1],\n                                 y=y_axis_failed,\n                                 name='failed')],\n                    layout=go.Layout(xaxis=dict(tickformat='%m-%d\\n%H:%M:%S',\n                                                autorange=True,\n                                                title='Time'),\n                                     yaxis=dict(tickformat=',d',\n                                                title='Running tasks.' ' Bin width: ' + num_to_timestamp(time_step).strftime('%Mm%Ss')),\n                                     annotations=[\n                                         dict(\n                                             x=0,\n                                             y=1.07,\n                                             showarrow=False,\n                                             text='Total Done: ' +\n                                             str(sum(y_axis_done)),\n                                             xref='paper',\n                                             yref='paper'\n                                         ),\n                                         dict(\n                                             x=0,\n                                             y=1.05,\n                                             showarrow=False,\n                                             text='Total Failed: ' +\n                                             str(sum(y_axis_failed)),\n                                             xref='paper',\n                                             yref='paper'\n                                         ),\n                    ],\n        barmode='stack',\n        title=\"Total tasks\"))\n\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n\n\ndef workflow_dag_plot(df_tasks, group_by_apps=True):\n    G = nx.DiGraph(directed=True)\n    nodes = df_tasks['task_id'].unique()\n    dic = df_tasks.set_index('task_id').to_dict()\n    G.add_nodes_from(nodes)\n\n    # Add edges or links between the nodes:\n    edges = []\n    for k, v in dic['task_depends'].items():\n        if v:\n            adj = v.split(\",\")\n            for e in adj:\n                edges.append((int(e), k))\n    G.add_edges_from(edges)\n\n    node_positions = nx.nx_pydot.pydot_layout(G, prog='dot')\n    node_traces = []\n\n    if group_by_apps:\n        groups_list = {app: i for i, app in enumerate(\n            df_tasks['task_func_name'].unique())}\n    else:\n        groups_list = {'Pending': (0, 'gray'), \"Running\": (1, 'blue'), 'Completed': (2, 'green')}\n\n    for k, _ in groups_list.items():\n        node_trace = go.Scatter(\n            x=[],\n            y=[],\n            text=[],\n            mode='markers',\n            textposition='top center',\n            textfont=dict(\n                family='arial',\n                size=18,\n                color='rgb(0,0,0)'\n            ),\n            hoverinfo='text',\n            name=k,          # legend app_name here\n            marker=dict(\n                showscale=False,\n                # color='rgb(200,0,0)',\n                size=11,\n                line=dict(width=1, color='rgb(0,0,0)')))\n        node_traces.append(node_trace)\n\n    for node in node_positions:\n        x, y = node_positions[node]\n        if group_by_apps:\n            name = dic['task_func_name'][node]\n            index = groups_list[name]\n        else:\n            if dic['task_time_returned'][node] is not None:\n                name = 'Completed'\n            elif dic['task_time_running'][node] is not None:\n                name = \"Running\"\n            elif dic['task_time_submitted'][node] is not None:\n                name = \"Pending\"\n            index, color = groups_list[name]\n            node_traces[index]['marker']['color'] = color\n        node_traces[index]['x'] += tuple([x])\n        node_traces[index]['y'] += tuple([y])\n        node_traces[index]['text'] += tuple(\n            [\"{}:{}\".format(dic['task_func_name'][node], node)])\n\n    # The edges will be drawn as lines:\n    edge_trace = go.Scatter(\n        x=[],\n        y=[],\n        line=dict(width=1, color='rgb(160,160,160)'),\n        hoverinfo='none',\n        # showlegend=False,\n        name='Dependency',\n        mode='lines')\n\n    for edge in G.edges:\n        x0, y0 = node_positions[edge[0]]\n        x1, y1 = node_positions[edge[1]]\n        edge_trace['x'] += tuple([x0, x1, None])\n        edge_trace['y'] += tuple([y0, y1, None])\n\n    # Create figure:\n    fig = go.Figure(data=[edge_trace] + node_traces,\n                    layout=go.Layout(\n                    title='Workflow DAG',\n                    titlefont=dict(size=16),\n                    showlegend=True,\n                    hovermode='closest',\n                    margin=dict(b=20, l=5, r=5, t=40),   # noqa: E741\n                    xaxis=dict(showgrid=False, zeroline=False,\n                               showticklabels=False),\n                    yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)))\n    return plot(fig, show_link=False, output_type=\"div\", include_plotlyjs=False)\n", "path": "parsl/monitoring/visualization/plots/default/workflow_plots.py"}]}