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
|
|
|
|