|
1
|
|
|
import pandas as pd |
|
2
|
|
|
import numpy as np |
|
3
|
|
|
import datetime |
|
4
|
|
|
import os, sys |
|
5
|
|
|
import re |
|
6
|
|
|
from os import listdir |
|
7
|
|
|
from os.path import isfile, join |
|
8
|
|
|
import pytest # automatic test finder and test runner |
|
9
|
|
|
|
|
10
|
|
|
# To import files from the parent directory |
|
11
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
12
|
|
|
|
|
13
|
|
|
import battdeg as bd |
|
14
|
|
|
from battdeg import pl_samples_file_reader |
|
15
|
|
|
from battdeg import date_time_converter |
|
16
|
|
|
from battdeg import get_dict_files |
|
17
|
|
|
from battdeg import concat_dict_dataframes |
|
18
|
|
|
from battdeg import get_cycle_capacities |
|
19
|
|
|
from battdeg import cx2_file_reader |
|
20
|
|
|
from battdeg import file_name_sorting |
|
21
|
|
|
from battdeg import reading_dataframes |
|
22
|
|
|
from battdeg import concat_df |
|
23
|
|
|
from battdeg import capacity |
|
24
|
|
|
from battdeg import data_formatting |
|
25
|
|
|
from battdeg import series_to_supervised |
|
26
|
|
|
from battdeg import long_short_term_memory |
|
27
|
|
|
from battdeg import model_training |
|
28
|
|
|
from battdeg import model_prediction |
|
29
|
|
|
from battdeg import file_reader |
|
30
|
|
|
|
|
31
|
|
|
# Path for data for testing |
|
32
|
|
|
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
33
|
|
|
module_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
34
|
|
|
model_path = join(module_dir,'models') |
|
35
|
|
|
data_path = join(module_dir, 'data') |
|
36
|
|
|
data_path_pl12_14 = join(data_path,'PL12') |
|
37
|
|
|
data_path_cx2 = join(data_path,'CX2_16') |
|
38
|
|
|
|
|
39
|
|
|
########################################################################### |
|
40
|
|
|
####################### Tests for `pl_samples_file_reader` ################ |
|
41
|
|
|
########################################################################### |
|
42
|
|
|
|
|
43
|
|
|
# This test will test the function `pl_samples_file_reader` for bad input |
|
44
|
|
|
def test_pl_samples_file_reader_BadIn(): |
|
45
|
|
|
|
|
46
|
|
|
# Inputs with wrong type for data_dir |
|
47
|
|
|
dd1 = 123 |
|
48
|
|
|
fnf1 = "PL12(4).csv" |
|
49
|
|
|
file_indices1 = [1, 2, 3] |
|
50
|
|
|
|
|
51
|
|
|
# Inputs with wrong type for input_file_indices |
|
52
|
|
|
dd2 = data_path_pl12_14 |
|
53
|
|
|
fnf2 = "PL12(4).csv" |
|
54
|
|
|
file_indices2 = 1 |
|
55
|
|
|
|
|
56
|
|
|
dd3 = data_path_pl12_14 |
|
57
|
|
|
fnf3 = "PL12(4).csv" |
|
58
|
|
|
file_indices3 = ['a', 'b', 'c'] |
|
59
|
|
|
|
|
60
|
|
|
# Inputs with wrong type for file_name_format |
|
61
|
|
|
dd4 = data_path_pl12_14 |
|
62
|
|
|
fnf4 = 123 |
|
63
|
|
|
file_indices4 = [1, 2, 3] |
|
64
|
|
|
|
|
65
|
|
|
# Inputs with wrong file not found error |
|
66
|
|
|
dd5 = data_path_pl12_14 |
|
67
|
|
|
fnf5 = "123" |
|
68
|
|
|
file_indices5 = [1, 2, 3] |
|
69
|
|
|
|
|
70
|
|
|
# The wrong type input should raise a TypeError |
|
71
|
|
|
with pytest.raises(TypeError): |
|
72
|
|
|
pl_samples_file_reader(dd1, fnf1, file_indices1) |
|
73
|
|
|
|
|
74
|
|
|
with pytest.raises(TypeError): |
|
75
|
|
|
pl_samples_file_reader(dd2, fnf2, file_indices2) |
|
76
|
|
|
|
|
77
|
|
|
with pytest.raises(TypeError): |
|
78
|
|
|
pl_samples_file_reader(dd3, fnf3, file_indices3) |
|
79
|
|
|
|
|
80
|
|
|
with pytest.raises(TypeError): |
|
81
|
|
|
pl_samples_file_reader(dd4, fnf4, file_indices4) |
|
82
|
|
|
|
|
83
|
|
|
with pytest.raises(FileNotFoundError): |
|
84
|
|
|
pl_samples_file_reader(dd5, fnf5, file_indices5) |
|
85
|
|
|
|
|
86
|
|
|
### TODO: Add tests for checking the validity of the path |
|
87
|
|
|
|
|
88
|
|
|
return |
|
89
|
|
|
|
|
90
|
|
|
|
|
91
|
|
|
# Test the output type of the function |
|
92
|
|
|
def test_pl_samples_file_reader_Type(): |
|
93
|
|
|
|
|
94
|
|
|
# Correct inputs |
|
95
|
|
|
dd1 = data_path_pl12_14 |
|
96
|
|
|
fnf1 = "PL12(4).csv" |
|
97
|
|
|
file_indices1 = [1, 2, 3] |
|
98
|
|
|
|
|
99
|
|
|
# Run the function with these inputs |
|
100
|
|
|
result = pl_samples_file_reader(dd1, fnf1, file_indices1) |
|
101
|
|
|
|
|
102
|
|
|
# Check if the output is of type pd.DataFrame |
|
103
|
|
|
assert isinstance(result, pd.DataFrame), "The type of the return value is not of type Pandas.DataFrame" |
|
104
|
|
|
|
|
105
|
|
|
return |
|
106
|
|
|
|
|
107
|
|
|
|
|
108
|
|
|
########################################################################### |
|
109
|
|
|
####################### Tests for `date_time_converter()` ################ |
|
110
|
|
|
########################################################################### |
|
111
|
|
|
|
|
112
|
|
|
# Test the date time converter function |
|
113
|
|
|
def test_date_time_converter_BadIn(): |
|
114
|
|
|
|
|
115
|
|
|
dt_in = 123 |
|
116
|
|
|
|
|
117
|
|
|
with pytest.raises(TypeError): |
|
118
|
|
|
date_time_converter(dt_in) |
|
119
|
|
|
|
|
120
|
|
|
return |
|
121
|
|
|
|
|
122
|
|
|
# Test the value of the date time returned is correct |
|
123
|
|
|
# Ideally should be done through hypothesis |
|
124
|
|
|
def test_date_time_converter_value(): |
|
125
|
|
|
|
|
126
|
|
|
dt_in = [731885.75] |
|
127
|
|
|
|
|
128
|
|
|
result = date_time_converter(dt_in) |
|
129
|
|
|
|
|
130
|
|
|
assert result == [datetime.datetime(2003, 10, 31, 18, 0)], "The date time returned is not correct" |
|
131
|
|
|
|
|
132
|
|
|
return |
|
133
|
|
|
|
|
134
|
|
|
########################################################################### |
|
135
|
|
|
####################### Tests for `get_dict_files()` ################ |
|
136
|
|
|
########################################################################### |
|
137
|
|
|
|
|
138
|
|
|
def test_get_dict_files_Type(): |
|
139
|
|
|
|
|
140
|
|
|
# Correct inputs |
|
141
|
|
|
dd1 = data_path_pl12_14 |
|
142
|
|
|
fnf1 = "PL12(4).csv" |
|
143
|
|
|
file_indices1 = [1, 2, 3] |
|
144
|
|
|
|
|
145
|
|
|
result = get_dict_files(dd1, fnf1, file_indices1) |
|
146
|
|
|
|
|
147
|
|
|
assert isinstance(result, dict), "The type of output from the function get_dict_files should be a dictionary" |
|
148
|
|
|
|
|
149
|
|
|
return |
|
150
|
|
|
|
|
151
|
|
|
########################################################################### |
|
152
|
|
|
####################### Tests for `concat_dict_dataframes()` ################ |
|
153
|
|
|
########################################################################### |
|
154
|
|
|
|
|
155
|
|
|
def test_concat_dict_dataframes_BadIn(): |
|
156
|
|
|
|
|
157
|
|
|
# Input not of the right type |
|
158
|
|
|
dict_ordered1 = 123 |
|
159
|
|
|
|
|
160
|
|
|
# Input not of the right type |
|
161
|
|
|
dict_ordered2 = {'a': pd.DataFrame()} |
|
162
|
|
|
|
|
163
|
|
|
# Input not of the right type |
|
164
|
|
|
dict_ordered3 = {1: [1, 2, 3]} |
|
165
|
|
|
|
|
166
|
|
|
# test that the dataframe has the named columns that we expect |
|
167
|
|
|
dict_ordered4 = {1: pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})} |
|
168
|
|
|
|
|
169
|
|
|
with pytest.raises(TypeError): |
|
170
|
|
|
concat_dict_dataframes(dict_ordered1) |
|
171
|
|
|
|
|
172
|
|
|
with pytest.raises(TypeError): |
|
173
|
|
|
concat_dict_dataframes(dict_ordered2) |
|
174
|
|
|
|
|
175
|
|
|
with pytest.raises(TypeError): |
|
176
|
|
|
concat_dict_dataframes(dict_ordered3) |
|
177
|
|
|
|
|
178
|
|
|
|
|
179
|
|
|
with pytest.raises(Exception, match = "the dataframe doesnt have the columns 'Cycle'" + |
|
180
|
|
|
", 'Charge_Ah', 'Discharge_Ah', " + |
|
181
|
|
|
"'Time_sec', 'Voltage_Volt', 'Current_Amp' "): |
|
182
|
|
|
concat_dict_dataframes(dict_ordered4) |
|
183
|
|
|
|
|
184
|
|
|
return |
|
185
|
|
|
|
|
186
|
|
|
|
|
187
|
|
|
# test whether the output type is a dataframe |
|
188
|
|
|
def test_concat_dict_dataframes_Type(): |
|
189
|
|
|
|
|
190
|
|
|
# Good input |
|
191
|
|
|
# dict_ordered1 = {1 : |
|
192
|
|
|
# result = |
|
193
|
|
|
return |
|
194
|
|
|
|
|
195
|
|
|
|
|
196
|
|
|
########################################################################### |
|
197
|
|
|
####################### Tests for `get_cycle_capacities()` ################ |
|
198
|
|
|
########################################################################### |
|
199
|
|
|
|
|
200
|
|
|
|
|
201
|
|
|
def test_get_cycle_capacities_Type(): |
|
202
|
|
|
|
|
203
|
|
|
|
|
204
|
|
|
return |
|
205
|
|
|
|
|
206
|
|
|
|
|
207
|
|
|
def test_get_cycle_capacities_Type(): |
|
208
|
|
|
|
|
209
|
|
|
df_out1 = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]}) |
|
210
|
|
|
|
|
211
|
|
|
with pytest.raises(Exception, match = "the dataframe doesnt have the columns 'Cycle'" + |
|
212
|
|
|
", 'Charge_Ah', 'Discharge_Ah', " + |
|
213
|
|
|
"'Time_sec', 'Voltage_Volt', 'Current_Amp' "): |
|
214
|
|
|
get_cycle_capacities(df_out1) |
|
215
|
|
|
|
|
216
|
|
|
return |
|
217
|
|
|
|
|
218
|
|
|
########################################################################### |
|
219
|
|
|
####################### Tests for `cx2_file_reader` ########################### |
|
220
|
|
|
########################################################################### |
|
221
|
|
|
|
|
222
|
|
|
# This test will test the function `cx2_file_reader` for bad input |
|
223
|
|
|
def test_cx2_file_reader_BadIn(): |
|
224
|
|
|
|
|
225
|
|
|
#Inputs with wrong type for data_dir |
|
226
|
|
|
dd1 = 12 |
|
227
|
|
|
fnf1 = 'CX2_16' |
|
228
|
|
|
sn1 = 1 |
|
229
|
|
|
|
|
230
|
|
|
#Input with wrong type of sheet name |
|
231
|
|
|
dd2 = data_path_cx2 |
|
232
|
|
|
fnf2 = 'CX2_16' |
|
233
|
|
|
sn2 = 123.5 |
|
234
|
|
|
|
|
235
|
|
|
#Input with wrong type of file name format |
|
236
|
|
|
dd3 = data_path_cx2 |
|
237
|
|
|
fnf3 = 123 |
|
238
|
|
|
sn3 = 1 |
|
239
|
|
|
|
|
240
|
|
|
#Input with wrong file not found error |
|
241
|
|
|
dd4 = data_path_cx2 |
|
242
|
|
|
fnf4 = "abc" |
|
243
|
|
|
sn4 = 1 |
|
244
|
|
|
|
|
245
|
|
|
#The wrong type input should raise a TypeError |
|
246
|
|
|
with pytest.raises(TypeError): |
|
247
|
|
|
cx2_file_reader(dd1, fnf1, sn1) |
|
248
|
|
|
|
|
249
|
|
|
with pytest.raises(TypeError): |
|
250
|
|
|
cx2_file_reader(dd2, fnf2, sn2) |
|
251
|
|
|
|
|
252
|
|
|
with pytest.raises(TypeError): |
|
253
|
|
|
cx2_file_reader(dd3, fnf3, sn3) |
|
254
|
|
|
|
|
255
|
|
|
with pytest.raises(FileNotFoundError): |
|
256
|
|
|
cx2_file_reader(dd4, fnf4, sn4) |
|
257
|
|
|
|
|
258
|
|
|
return |
|
259
|
|
|
|
|
260
|
|
|
# Test the output type of the function |
|
261
|
|
|
#Correct inputs |
|
262
|
|
|
dd = data_path |
|
263
|
|
|
fnf = 'CX2_16' |
|
264
|
|
|
data_path_cx2_16 = join(data_path,'CX2_16') |
|
265
|
|
|
sn = 1 |
|
266
|
|
|
# Run the function with these inputs |
|
267
|
|
|
result = cx2_file_reader(dd, fnf, sn) |
|
268
|
|
|
def test_cx2_file_reader(): |
|
269
|
|
|
assert isinstance(result,pd.DataFrame), 'Output is not a dataframe' |
|
270
|
|
|
|
|
271
|
|
|
# Test the output of the function 'file_name_sorting' |
|
272
|
|
|
files = listdir(data_path_cx2_16) |
|
273
|
|
|
file_name_list = list(filter(lambda x: x[-5:]=='.xlsx' , files)) |
|
274
|
|
|
def test_file_name_sorting(): |
|
275
|
|
|
sorted_list = file_name_sorting(file_name_list) |
|
276
|
|
|
assert isinstance(sorted_list,np.ndarray),'Output is not a list' |
|
277
|
|
|
|
|
278
|
|
|
# Test the output of the function 'reading_dataframes' |
|
279
|
|
|
file_names = file_name_sorting(file_name_list) |
|
280
|
|
|
Sheet_Name = 1 |
|
281
|
|
|
path = join(data_path,'CX2_16') |
|
282
|
|
|
df = reading_dataframes(file_names, Sheet_Name, path) |
|
283
|
|
|
def test_reading_dataframes(): |
|
284
|
|
|
assert isinstance(df, dict), 'Output is not a dictionary of dataframes' |
|
285
|
|
|
|
|
286
|
|
|
# Test the output of the function 'concat_df' |
|
287
|
|
|
merged_df = concat_df(df) |
|
288
|
|
|
def test_concat_df(): |
|
289
|
|
|
assert isinstance(merged_df, pd.DataFrame),'Output is not a dataframe' |
|
290
|
|
|
|
|
291
|
|
|
# Test the output of the function 'capacity' |
|
292
|
|
|
capacity_df = capacity(merged_df) |
|
293
|
|
|
def test_capacity(): |
|
294
|
|
|
assert isinstance(capacity_df,pd.DataFrame),'Output is not a dataframe' |
|
295
|
|
|
|
|
296
|
|
|
# Test the output of the function 'data_formatting' |
|
297
|
|
|
formatted_df = data_formatting(capacity_df) |
|
298
|
|
|
def test_data_formatting(): |
|
299
|
|
|
assert isinstance(formatted_df,pd.DataFrame),'Output is not a dataframe' |
|
300
|
|
|
assert len(formatted_df.columns) == 3,'The number of columns in the output is not 3 as expected' |
|
301
|
|
|
|
|
302
|
|
|
# Test the output of the function 'series_to_supervised' |
|
303
|
|
|
training_data = series_to_supervised(formatted_df) |
|
304
|
|
|
def test_series_to_supervised(): |
|
305
|
|
|
assert isinstance(training_data, pd.DataFrame),'Output is not a dataframe' |
|
306
|
|
|
assert len(training_data.columns) == 4,'The number of columns in the output is not 4 as expected' |
|
307
|
|
|
|
|
308
|
|
|
# Test the output of the function 'series_to_supervised' for n_out=2 |
|
309
|
|
|
# This will test the else condition |
|
310
|
|
|
training_data2 = series_to_supervised(formatted_df, n_out=2) |
|
311
|
|
|
def test_series_to_supervised2(): |
|
312
|
|
|
assert isinstance(training_data2, pd.DataFrame),'Output is not a dataframe' |
|
313
|
|
|
|
|
314
|
|
|
# Test the output of the function 'long_short_term_memory' |
|
315
|
|
|
model_loss, yhat = long_short_term_memory(training_data) |
|
316
|
|
|
def test_long_short_term_memory(): |
|
317
|
|
|
assert isinstance(yhat, np.ndarray),'Response of the test dataset is not an array' |
|
318
|
|
|
assert isinstance(model_loss, dict),'Loss function is not a dictionary' |
|
319
|
|
|
assert yhat.shape[1] == 1,'The number of columns in the output is not 1 as expected' |
|
320
|
|
|
|
|
321
|
|
|
# Test the output of the function 'model_training' |
|
322
|
|
|
model_loss, yhat = model_training(dd, fnf, sn) |
|
323
|
|
|
def test_model_training(): |
|
324
|
|
|
assert isinstance(yhat, np.ndarray),'Response of the test dataset is not an array' |
|
325
|
|
|
assert isinstance(model_loss, dict),'Loss function is not a dictionary' |
|
326
|
|
|
assert yhat.shape[1] == 1,'The number of columns in the output is not 1 as expected' |
|
327
|
|
|
|
|
328
|
|
|
# # Test the output of the function 'model_predict' |
|
329
|
|
|
y_predicted = model_prediction(formatted_df) |
|
330
|
|
|
def test_model_prediction(): |
|
331
|
|
|
assert isinstance(y_predicted, np.ndarray),'Response of the test dataset is not an array' |
|
332
|
|
|
assert y_predicted.shape[1] == 1,'The number of columns in the output is not 1 as expected' |
|
333
|
|
|
|
|
334
|
|
|
# Test the output of the function 'file_reader' |
|
335
|
|
|
file_indices1 = [1, 2, 3] |
|
336
|
|
|
sheet_name = 1 |
|
337
|
|
|
data_dir = data_path |
|
338
|
|
|
file_name_format = 'CX2_16' |
|
339
|
|
|
df_output = file_reader(data_dir, file_name_format, sheet_name, file_indices1) |
|
340
|
|
|
def test_file_reader(): |
|
341
|
|
|
assert isinstance(df_output,pd.DataFrame), 'Output is not a dataframe' |
|
342
|
|
|
|
|
343
|
|
|
# Correct inputs - to test for the `else` in file_reader |
|
344
|
|
|
dd1 = data_path_pl12_14 |
|
345
|
|
|
file_name_format_PL = 'PL12(4).csv' |
|
346
|
|
|
file_indices1 = [1, 2, 3] |
|
347
|
|
|
df_output2 = file_reader(data_path_pl12_14, file_name_format_PL, sheet_name, file_indices1) |
|
348
|
|
|
|
|
349
|
|
|
def test_file_reader2(): |
|
350
|
|
|
assert isinstance(df_output2, pd.DataFrame), 'Output is not a dataframe' |
|
351
|
|
|
|