Test Failed
Push — develop ( d7cf39...faa4bd )
by Nicolas
04:34 queued 10s
created

glances/exports/glances_csv.py (23 issues)

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2019 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""CSV interface class."""
21
22
import os.path
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
23
import csv
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
24
import sys
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
25
import time
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
26
27
from glances.compat import PY3, iterkeys, itervalues
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
28
from glances.logger import logger
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
29
from glances.exports.glances_export import GlancesExport
0 ignored issues
show
import missing from __future__ import absolute_import
Loading history...
30
31
32
class Export(GlancesExport):
33
34
    """This class manages the CSV export module."""
35
36
    def __init__(self, config=None, args=None):
37
        """Init the CSV export IF."""
38
        super(Export, self).__init__(config=config, args=args)
39
40
        # CSV file name
41
        self.csv_filename = args.export_csv_file
42
43
        # Set the CSV output file
44
        # (see https://github.com/nicolargo/glances/issues/1525)
45
        if not os.path.isfile(self.csv_filename) or args.export_csv_overwrite:
46
            # File did not exist, create it
47
            file_mode = 'w'
48
            self.old_header = None
49
        else:
50
            # A CSV file already exit, append new data
51
            file_mode = 'a'
52
            # Header will be check later
53
            # Get the existing one
54
            try:
55
                self.csv_file = open_csv_file(self.csv_filename, 'r')
56
                reader = csv.reader(self.csv_file)
57
            except IOError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
58
                logger.critical("Cannot open existing CSV file: {}".format(e))
0 ignored issues
show
Use formatting in logging functions and pass the parameters as arguments
Loading history...
59
                sys.exit(2)
60
            self.old_header = next(reader, None)
0 ignored issues
show
The variable reader does not seem to be defined for all execution paths.
Loading history...
61
            self.csv_file.close()
62
63
        try:
64
            self.csv_file = open_csv_file(self.csv_filename, file_mode)
65
            self.writer = csv.writer(self.csv_file)
66
        except IOError as e:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
67
            logger.critical("Cannot create the CSV file: {}".format(e))
0 ignored issues
show
Use formatting in logging functions and pass the parameters as arguments
Loading history...
68
            sys.exit(2)
69
70
        logger.info("Stats exported to CSV file: {}".format(self.csv_filename))
0 ignored issues
show
Use formatting in logging functions and pass the parameters as arguments
Loading history...
71
72
        self.export_enable = True
73
74
        self.first_line = True
75
76
    def exit(self):
77
        """Close the CSV file."""
78
        logger.debug("Finalise export interface %s" % self.export_name)
0 ignored issues
show
Coding Style Best Practice introduced by
Specify string format arguments as logging function parameters
Loading history...
79
        self.csv_file.close()
80
81
    def update(self, stats):
82
        """Update stats in the CSV output file."""
83
        # Get the stats
84
        all_stats = stats.getAllExportsAsDict(plugin_list=self.plugins_to_export())
0 ignored issues
show
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
85
86
        # Init data with timestamp (issue#708)
87
        if self.first_line:
88
            csv_header = ['timestamp']
89
        csv_data = [time.strftime('%Y-%m-%d %H:%M:%S')]
90
91
        # Loop over plugins to export
92
        for plugin in self.plugins_to_export():
93
            if isinstance(all_stats[plugin], list):
94
                for stat in all_stats[plugin]:
95
                    # First line: header
96
                    if self.first_line:
97
                        csv_header += ('{}_{}_{}'.format(
0 ignored issues
show
The variable csv_header does not seem to be defined in case self.first_line on line 87 is False. Are you sure this can never be the case?
Loading history...
98
                            plugin, self.get_item_key(stat), item) for item in stat)
0 ignored issues
show
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
The variable item does not seem to be defined for all execution paths.
Loading history...
99
                    # Others lines: stats
100
                    csv_data += itervalues(stat)
101
            elif isinstance(all_stats[plugin], dict):
102
                # First line: header
103
                if self.first_line:
104
                    fieldnames = iterkeys(all_stats[plugin])
105
                    csv_header += ('{}_{}'.format(plugin, fieldname)
0 ignored issues
show
The variable fieldname does not seem to be defined for all execution paths.
Loading history...
106
                                   for fieldname in fieldnames)
107
                # Others lines: stats
108
                csv_data += itervalues(all_stats[plugin])
109
110
        # Export to CSV
111
        # Manage header
112
        if self.first_line:
113
            if self.old_header is None:
114
                # New file, write the header on top on the CSV file
115
                self.writer.writerow(csv_header)
116
            # File already exist, check if header are compatible
117
            if self.old_header != csv_header:
118
                # Header are differents, log an error and do not write data
119
                logger.error("Cannot append data to existing CSV file. Headers are differents.")
0 ignored issues
show
This line is too long as per the coding-style (96/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
120
                logger.debug("Old header: {}".format(self.old_header))
0 ignored issues
show
Use formatting in logging functions and pass the parameters as arguments
Loading history...
121
                logger.debug("New header: {}".format(csv_header))
0 ignored issues
show
Use formatting in logging functions and pass the parameters as arguments
Loading history...
122
            else:
123
                # Header are equals, ready to write data
124
                self.old_header = None
125
            # Only do this once
126
            self.first_line = False
127
        # Manage data
128
        if self.old_header is None:
129
            self.writer.writerow(csv_data)
130
            self.csv_file.flush()
131
132
133
def open_csv_file(file_name, file_mode):
0 ignored issues
show
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
134
    if PY3:
135
        csv_file = open(file_name, file_mode, newline='')
136
    else:
137
        csv_file = open(file_name, file_mode + 'b')
138
    return csv_file
139