|
1
|
|
|
import dash_resumable_upload |
|
2
|
|
|
import dash |
|
3
|
|
|
import dash_html_components as html |
|
4
|
|
|
from dash.dependencies import Input, Output |
|
5
|
|
|
import base64 |
|
6
|
|
|
from os import listdir,system |
|
7
|
|
|
import dash_table_experiments as dt |
|
8
|
|
|
import dash_core_components as dcc |
|
9
|
|
|
from os.path import isfile, join |
|
10
|
|
|
import shutil |
|
11
|
|
|
import time |
|
12
|
|
|
import core |
|
13
|
|
|
import io |
|
14
|
|
|
import plotly.graph_objs as go |
|
15
|
|
|
import pandas as pd |
|
16
|
|
|
|
|
17
|
|
|
try: |
|
18
|
|
|
system("rm -r uploads") |
|
19
|
|
|
except: |
|
20
|
|
|
pass |
|
21
|
|
|
|
|
22
|
|
|
app = dash.Dash('') |
|
23
|
|
|
|
|
24
|
|
|
#external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css', 'https://codepen.io/rmarren1/pen/eMQKBW.css'] |
|
25
|
|
|
#app = dash.Dash(__name__, external_stylesheets=external_stylesheets) |
|
26
|
|
|
|
|
27
|
|
|
colors = { |
|
28
|
|
|
'background': '#ECF0F1', |
|
29
|
|
|
'text': '#800000' |
|
30
|
|
|
} |
|
31
|
|
|
|
|
32
|
|
|
image_filename = 'Logo.png' # replace with your own image |
|
33
|
|
|
encoded_image = base64.b64encode(open(image_filename, 'rb').read()).decode('ascii') |
|
34
|
|
|
|
|
35
|
|
|
dash_resumable_upload.decorate_server(app.server, "uploads") |
|
36
|
|
|
|
|
37
|
|
|
app.scripts.config.serve_locally = True # Uploaded to npm, this can work online now too. |
|
38
|
|
|
|
|
39
|
|
|
|
|
40
|
|
|
#app.css.append_css({ |
|
41
|
|
|
# "external_url": "https://codepen.io/rmarren1/pen/eMQKBW.css" |
|
42
|
|
|
#}) |
|
43
|
|
|
|
|
44
|
|
|
app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[ |
|
45
|
|
|
html.H1( |
|
46
|
|
|
children='VoltCycle', |
|
47
|
|
|
style={ |
|
48
|
|
|
'textAlign': 'center', |
|
49
|
|
|
'color': colors['text'] |
|
50
|
|
|
} |
|
51
|
|
|
), |
|
52
|
|
|
|
|
53
|
|
|
html.Div([ |
|
54
|
|
|
html.Img(draggable=True, style={ |
|
55
|
|
|
'height': '20%', |
|
56
|
|
|
'width': '20%' |
|
57
|
|
|
}, src='data:image/png;base64,{}'.format(encoded_image)) |
|
58
|
|
|
], style={'textAlign': 'center'}), |
|
59
|
|
|
|
|
60
|
|
|
html.H2(children='A Tool for Accelerating the Analysis of Cyclic Voltammetry Data', style={ |
|
61
|
|
|
'textAlign': 'center', |
|
62
|
|
|
'color': colors['text'] |
|
63
|
|
|
}), |
|
64
|
|
|
html.Br(), |
|
65
|
|
|
html.Div([ |
|
66
|
|
|
html.Link(rel='stylesheet', href='https://codepen.io/rmarren1/pen/eMQKBW.css'), |
|
67
|
|
|
dash_resumable_upload.Upload( |
|
68
|
|
|
id='upload', |
|
69
|
|
|
maxFiles=1, |
|
70
|
|
|
maxFileSize=1024*1024*1000, # 100 MB |
|
71
|
|
|
service="/upload_resumable", |
|
72
|
|
|
textLabel="Upload Files", |
|
73
|
|
|
startButton=False) |
|
74
|
|
|
]), |
|
75
|
|
|
html.Div(id='output_uploaded_file'), |
|
76
|
|
|
html.Br(), |
|
77
|
|
|
html.H2( |
|
78
|
|
|
children='Select File to Analyze', |
|
79
|
|
|
style={ |
|
80
|
|
|
'textAlign': 'center', |
|
81
|
|
|
'color': colors['text'] |
|
82
|
|
|
} |
|
83
|
|
|
), |
|
84
|
|
|
html.Div([ |
|
85
|
|
|
dcc.Dropdown(id='files_dropdown') |
|
86
|
|
|
],style={'width': '70%', 'height': '40', 'display': 'inline-block', 'textAlign': 'center'} |
|
87
|
|
|
), |
|
88
|
|
|
html.Div([ |
|
89
|
|
|
html.Br(), |
|
90
|
|
|
dcc.Graph(id='CV_graph'), |
|
91
|
|
|
],style={ |
|
92
|
|
|
'columnCount': 1, |
|
93
|
|
|
'width':'70%', |
|
94
|
|
|
'height': '80%', |
|
95
|
|
|
} |
|
96
|
|
|
), |
|
97
|
|
|
|
|
98
|
|
|
|
|
99
|
|
|
html.Div([ |
|
100
|
|
|
html.H4( |
|
101
|
|
|
children='CV DataTable', |
|
102
|
|
|
style={ |
|
103
|
|
|
'textAlign': 'center', |
|
104
|
|
|
'color': colors['text'] |
|
105
|
|
|
} |
|
106
|
|
|
), |
|
107
|
|
|
dt.DataTable( |
|
108
|
|
|
#rows=charge.to_dict('records'), #converts df to dict |
|
109
|
|
|
rows=[{}], |
|
110
|
|
|
#columns=sorted(charge.columns), #sorts columns |
|
111
|
|
|
row_selectable=True, |
|
112
|
|
|
filterable=True, |
|
113
|
|
|
selected_row_indices=[], |
|
114
|
|
|
id='datatable_initial' |
|
115
|
|
|
), |
|
116
|
|
|
html.Div(id='selected-indexes'), |
|
117
|
|
|
|
|
118
|
|
|
], |
|
119
|
|
|
style={ |
|
120
|
|
|
'width': '98%', |
|
121
|
|
|
#'height': '60px', |
|
122
|
|
|
#'lineHeight': '60px', |
|
123
|
|
|
'margin': '10px' |
|
124
|
|
|
}, |
|
125
|
|
|
) |
|
126
|
|
|
|
|
127
|
|
|
]) |
|
128
|
|
|
|
|
129
|
|
|
|
|
130
|
|
|
#encode = base64.b64encode( |
|
131
|
|
|
# open("uploads/%s" % (x), 'rb').read()).decode('ascii') |
|
132
|
|
|
#return "data:image/jpg;base64,{}".format(encode) |
|
133
|
|
|
|
|
134
|
|
|
|
|
135
|
|
|
def parse_contents(value): |
|
136
|
|
|
|
|
137
|
|
|
lines1 = base64.b64encode(open("uploads/%s" % (value), 'rb').read()) |
|
138
|
|
|
lines2 = base64.b64decode(lines1).decode('utf-8').split('\n') |
|
139
|
|
|
#lines2 = lines1.decode('utf-8') |
|
140
|
|
|
#lines = io.StringIO(lines2) |
|
141
|
|
|
dict_1, n_cycle = core.read_file_dash(lines2) |
|
142
|
|
|
print(n_cycle) |
|
143
|
|
|
df = core.data_frame(dict_1, 1) |
|
144
|
|
|
return df |
|
145
|
|
|
|
|
146
|
|
|
|
|
147
|
|
|
def data_analysis(df): |
|
148
|
|
|
results_dict = {} |
|
149
|
|
|
|
|
150
|
|
|
# df = main.data_frame(dict_1,1) |
|
151
|
|
|
x = df['Potential'] |
|
152
|
|
|
y = df['Current'] |
|
153
|
|
|
# Peaks are here [list] |
|
154
|
|
|
peak_index = core.peak_detection_fxn(y) |
|
155
|
|
|
# Split x,y to get baselines |
|
156
|
|
|
x1,x2 = core.split(x) |
|
157
|
|
|
y1,y2 = core.split(y) |
|
158
|
|
|
y_base1 = core.linear_background(x1,y1) |
|
159
|
|
|
y_base2 = core.linear_background(x2,y2) |
|
160
|
|
|
# Calculations based on baseline and peak |
|
161
|
|
|
values = core.peak_values(x,y) |
|
162
|
|
|
Et = values[0] |
|
163
|
|
|
Eb = values[2] |
|
164
|
|
|
dE = core.del_potential(x,y) |
|
165
|
|
|
half_E = min(Et,Eb) + core.half_wave_potential(x,y) |
|
166
|
|
|
ia = core.peak_heights(x,y)[0] |
|
167
|
|
|
ic = core.peak_heights(x,y)[1] |
|
168
|
|
|
ratio_i = core.peak_ratio(x,y) |
|
169
|
|
|
results_dict['Peak Current Ratio'] = ratio_i |
|
170
|
|
|
results_dict['Ipc'] = ic |
|
171
|
|
|
results_dict['Ipa'] = ia |
|
172
|
|
|
results_dict['Epc'] = Eb |
|
173
|
|
|
results_dict['Epa'] = Et |
|
174
|
|
|
results_dict['∆E'] = dE |
|
175
|
|
|
results_dict['Redox Potential'] = half_E |
|
176
|
|
|
if dE>0.3: |
|
177
|
|
|
results_dict['Reversible'] = 'No' |
|
178
|
|
|
else: |
|
179
|
|
|
results_dict['Reversible'] = 'Yes' |
|
180
|
|
|
|
|
181
|
|
|
if half_E>0 and 'Yes' in results_dict.values(): |
|
182
|
|
|
results_dict['Type'] = 'Catholyte' |
|
183
|
|
|
elif 'Yes' in results_dict.values(): |
|
184
|
|
|
results_dict['Type'] = 'Anolyte' |
|
185
|
|
|
return results_dict |
|
186
|
|
|
|
|
187
|
|
|
|
|
188
|
|
|
@app.callback(Output('output_uploaded_file', 'children'), |
|
189
|
|
|
[Input('upload', 'fileNames')]) |
|
190
|
|
|
def display_files(fileNames): |
|
191
|
|
|
if fileNames is not None: |
|
192
|
|
|
#return html.Ul([html.Li( |
|
193
|
|
|
# html.Img(height="50", width="100", src=get_img(x))) for x in fileNames]) |
|
194
|
|
|
return html.Ul([html.Li(html.A(x), style={'textAlign': 'center'}) for x in fileNames]) |
|
195
|
|
|
return html.Ul(html.Li("No Files Uploaded Yet!"), style={'textAlign': 'center'}) |
|
196
|
|
|
|
|
197
|
|
|
|
|
198
|
|
View Code Duplication |
@app.callback(Output('files_dropdown', 'options'), |
|
|
|
|
|
|
199
|
|
|
[Input('upload','fileNames')]) |
|
200
|
|
|
def dropdown_files(fileNames): |
|
201
|
|
|
# time.sleep(5) |
|
202
|
|
|
mypath='./uploads/' |
|
203
|
|
|
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] |
|
204
|
|
|
#print(onlyfiles) |
|
205
|
|
|
#onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] |
|
206
|
|
|
#options=[{'label': i, 'value': i} for i in onlyfiles] |
|
207
|
|
|
#print(options) |
|
208
|
|
|
#return {'options':options} |
|
209
|
|
|
return [{'label': i, 'value': i} for i in onlyfiles] |
|
210
|
|
|
|
|
211
|
|
|
|
|
212
|
|
|
@app.callback( #update charge datatable |
|
213
|
|
|
Output('datatable_initial', 'rows'), |
|
214
|
|
|
[Input('files_dropdown', 'value')]) |
|
215
|
|
|
|
|
216
|
|
|
def update_table1(value): |
|
217
|
|
|
#for line in lines3: |
|
218
|
|
|
# print(line) |
|
219
|
|
|
#print(type(lines3)) |
|
220
|
|
|
df = parse_contents(value) |
|
221
|
|
|
print(df.head()) |
|
222
|
|
|
final_dict = data_analysis(df) |
|
223
|
|
|
#print(final_dict) |
|
224
|
|
|
#df1 = pd.DataFrame.from_dict(final_dict) |
|
225
|
|
|
df1=pd.DataFrame.from_records([final_dict]) |
|
226
|
|
|
#print(df1) |
|
227
|
|
|
#data = parse_contents(contents, filename, date) |
|
228
|
|
|
#charge, discharge = ccf.sep_char_dis(data) |
|
229
|
|
|
return df1.to_dict('records') |
|
230
|
|
|
|
|
231
|
|
|
@app.callback( |
|
232
|
|
|
Output('CV_graph', 'figure'), |
|
233
|
|
|
[Input('files_dropdown', 'value')]) |
|
234
|
|
|
def update_figure(value): |
|
235
|
|
|
df = parse_contents(value) |
|
236
|
|
|
|
|
237
|
|
|
return { |
|
238
|
|
|
'data': [go.Scatter( |
|
239
|
|
|
x = df['Potential'], |
|
240
|
|
|
y = df['Current'], |
|
241
|
|
|
marker={ |
|
242
|
|
|
'size': 15, |
|
243
|
|
|
'opacity': 0.5, |
|
244
|
|
|
'color' : '#FF851B' |
|
245
|
|
|
} |
|
246
|
|
|
)], |
|
247
|
|
|
#'layout' : {'Dash'} |
|
248
|
|
|
'layout': go.Layout( |
|
249
|
|
|
xaxis={'title': 'Voltage (V)'}, |
|
250
|
|
|
yaxis={'title': 'Current (A)'}, |
|
251
|
|
|
margin={'l': 40, 'b': 40, 't': 10, 'r': 10}, |
|
252
|
|
|
# #legend={'x': 0, 'y': 1}, |
|
253
|
|
|
hovermode='closest', |
|
254
|
|
|
) |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
|
|
258
|
|
|
if __name__ == '__main__': |
|
259
|
|
|
app.run_server(debug=True) |
|
260
|
|
|
|