Passed
Push — development/test ( ce11cc...42d9fd )
by Daniel
03:05
created

DataInputOutput.fn_save_data_frame_to_csv()   A

Complexity

Conditions 4

Size

Total Lines 13
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 13
rs 9.75
c 0
b 0
f 0
cc 4
nop 5
1
"""
2
Data Input Output class
3
"""
4
# package to add support for multi-language (i18n)
5
import gettext
6
# package to handle files/folders and related metadata/operations
7
import os
8
# package facilitating Data Frames manipulation
9
import pandas
10
11
12
class DataInputOutput:
13
    locale = None
14
15
    def __init__(self, default_language='en_US'):
16
        current_script = os.path.basename(__file__).replace('.py', '')
17
        lang_folder = os.path.join(os.path.dirname(__file__), current_script + '_Locale')
18
        self.locale = gettext.translation(current_script, lang_folder, languages=[default_language])
19
20
    @staticmethod
21
    def fn_add_missing_defaults_to_dict_message(in_dict):
22
        if 'field delimiter' not in in_dict:
23
            in_dict['field delimiter'] = os.pathsep
24
        if 'compression' not in in_dict:
25
            in_dict['compression'] = 'infer'
26
        return in_dict
27
28
    def fn_build_feedback_for_logger(self, operation_details):
29
        messages = {}
30
        if operation_details['operation'] == 'load':
31
            files_counted = str(operation_details['files counted'])
32
            messages = {
33
                'failed': self.locale.gettext('Error encountered on loading Pandas Data Frame '
34
                                              + 'from {file_type} file type (see below)'),
35
                'success': self.locale.gettext(
36
                    'All {files_counted} files of type {file_type} '
37
                    + 'successfully added to a Pandas Data Frame').replace('{files_counted}',
38
                                                                           files_counted)
39
            }
40
        elif operation_details['operation'] == 'save':
41
            messages = {
42
                'failed': self.locale.gettext('Error encountered on saving Pandas Data Frame '
43
                                              + 'into a {file_type} file type (see below)'),
44
                'success': self.locale.gettext(
45
                    'Pandas Data Frame has just been saved to file "{file_name}", '
46
                    + 'considering {file_type} as file type').replace('{file_name}',
47
                                                                      operation_details['name']),
48
            }
49
        messages['failed'].replace('{file_type}',  operation_details['format'].upper())
50
        messages['success'].replace('{file_type}',  operation_details['format'].upper())
51
        return messages
52
53
    def fn_file_operation_logger(self, local_logger, in_logger_dict):
54
        messages = self.fn_build_feedback_for_logger(in_logger_dict)
55
        if in_logger_dict['error details'] is None:
56
            local_logger.info(messages['success'])
57
        else:
58
            local_logger.error(messages['failed'])
59
            local_logger.error(in_logger_dict['error details'])
60
61
    def fn_load_file_into_data_frame(self, in_logger, timer, in_dict):
62
        timer.start()
63
        in_dict = self.fn_add_missing_defaults_to_dict_message(in_dict)
64
        in_dict.update({'operation': 'load'})
65
        details_for_logger = self.fn_pack_dict_message(in_dict, in_dict['file list'])
66
        out_df = None
67
        try:
68
            if in_dict['format'].lower() == 'csv':
69
                out_df = pandas.concat([pandas.read_csv(filepath_or_buffer=crt_file,
70
                                                        delimiter=in_dict['field delimiter'],
71
                                                        cache_dates=True,
72
                                                        index_col=None,
73
                                                        memory_map=True,
74
                                                        low_memory=False,
75
                                                        encoding='utf-8',
76
                                                        ) for crt_file in in_dict['file list']])
77
            elif in_dict['format'].lower() == 'excel':
78
                out_df = pandas.concat([pandas.read_excel(io=crt_file,
79
                                                          verbose=True,
80
                                                          ) for crt_file in in_dict['file list']])
81
            elif in_dict['format'].lower() == 'pickle':
82
                out_df = pandas.concat([pandas.read_pickle(filepath_or_buffer=crt_file,
83
                                                           compression=in_dict['compression'],
84
                                                           ) for crt_file in in_dict['file list']])
85
        except Exception as err:
86
            details_for_logger['error details'] = err
87
        self.fn_file_operation_logger(in_logger, details_for_logger)
88
        timer.stop()
89
        return out_df
90
91
    @staticmethod
92
    def fn_pack_dict_message(in_dict, in_file_list):
93
        return {
94
            'compression': in_dict['compression'],
95
            'field delimiter': in_dict['field delimiter'],
96
            'files counted': len(in_file_list),
97
            'error details': None,
98
            'name': in_dict['name'],
99
            'format': in_dict['format'],
100
            'operation': in_dict['operation'],
101
        }
102
103
    def fn_store_data_frame_to_file(self, in_logger, timer, in_data_frame, in_file_details):
104
        timer.start()
105
        if self.fn_store_data_frame_to_file_validation(in_logger, in_file_details):
106
            in_dict = self.fn_add_missing_defaults_to_dict_message(in_file_details)
107
            in_dict.update({'operation': 'save'})
108
            in_dict = self.fn_pack_dict_message(in_dict, [in_file_details['name']])
109
            self.fn_store_data_frame_to_known_file_format(in_logger, in_data_frame, in_dict)
110
        timer.stop()
111
112
    def fn_store_data_frame_to_known_file_format(self, in_logger, in_data_frame, in_dict):
113
        try:
114
            if in_dict['format'].lower() == 'csv':
115
                in_data_frame.to_csv(path_or_buf=in_dict['name'],
116
                                     sep=in_dict['field delimiter'],
117
                                     header=True,
118
                                     index=False,
119
                                     encoding='utf-8')
120
            elif in_dict['format'].lower() == 'excel':
121
                in_data_frame.to_excel(excel_writer=in_dict['name'],
122
                                       engine='xlsxwriter',
123
                                       freeze_panes=(1, 1),
124
                                       verbose=True)
125
            elif in_dict['format'].lower() == 'pickle':
126
                in_data_frame.to_pickle(path=in_dict['name'],
127
                                        compression=in_dict['compression'])
128
        except Exception as err:
129
            in_dict['error details'] = err
130
        self.fn_file_operation_logger(in_logger, in_dict)
131
132
    def fn_store_data_frame_to_file_validation(self, local_logger, in_file_details):
133
        given_format_is_implemented = False
134
        if 'format' in in_file_details:
135
            implemented_file_formats = ['csv', 'excel', 'pickle']
136
            given_format = in_file_details['format'].lower()
137
            given_format_is_implemented = True
138
            if given_format not in implemented_file_formats:
139
                given_format_is_implemented = False
140
                local_logger.error(self.locale.gettext(
141
                    'File "format" attribute has a value of "{format_value}" '
142
                    + 'which is not among currently implemented values: '
143
                    + '"{implemented_file_formats}", '
144
                    + 'therefore desired file operation is not possible')
145
                                   .replace('{format_value}', given_format)
146
                                   .replace('{implemented_file_formats}',
147
                                            '", "'.join(implemented_file_formats)))
148
        else:
149
            local_logger.error(self.locale.gettext(
150
                    'File "format" attribute is mandatory in the file setting, but missing, '
151
                    + 'therefore desired file operation is not possible'))
152
        return given_format_is_implemented
153