Test Failed
Push — master ( 7e7379...128504 )
by Nicolas
03:31
created

glances.compat.pretty_date()   F

Complexity

Conditions 16

Size

Total Lines 42
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 34
nop 1
dl 0
loc 42
rs 2.4
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like glances.compat.pretty_date() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
# flake8: noqa
21
# pylint: skip-file
22
"""Python 2/3 compatibility shims."""
23
24
from __future__ import print_function, unicode_literals
25
26
import operator
27
import sys
28
import unicodedata
29
import types
30
import subprocess
31
import os
32
from datetime import datetime
33
34
from glances.logger import logger
35
36
PY3 = sys.version_info[0] == 3
37
38
if PY3:
39
    import queue
40
    from configparser import ConfigParser, NoOptionError, NoSectionError
41
    from statistics import mean
42
    from xmlrpc.client import Fault, ProtocolError, ServerProxy, Transport, Server
43
    from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
44
    from urllib.request import urlopen
45
    from urllib.error import HTTPError, URLError
46
    from urllib.parse import urlparse
47
48
    # Correct issue #1025 by monkey path the xmlrpc lib
49
    from defusedxml.xmlrpc import monkey_patch
50
51
    monkey_patch()
52
53
    input = input
54
    range = range
55
    map = map
56
57
    text_type = str
58
    binary_type = bytes
59
    bool_type = bool
60
    long = int
61
62
    PermissionError = OSError
63
64
    viewkeys = operator.methodcaller('keys')
65
    viewvalues = operator.methodcaller('values')
66
    viewitems = operator.methodcaller('items')
67
68
    def printandflush(string):
69
        """Print and flush (used by stdout* outputs modules)"""
70
        print(string, flush=True)
71
72
    def to_ascii(s):
73
        """Convert the bytes string to a ASCII string
74
75
        Useful to remove accent (diacritics)
76
        """
77
        if isinstance(s, binary_type):
78
            return s.decode()
79
        return s.encode('ascii', 'ignore').decode()
80
81
    def listitems(d):
82
        return list(d.items())
83
84
    def listkeys(d):
85
        return list(d.keys())
86
87
    def listvalues(d):
88
        return list(d.values())
89
90
    def iteritems(d):
91
        return iter(d.items())
92
93
    def iterkeys(d):
94
        return iter(d.keys())
95
96
    def itervalues(d):
97
        return iter(d.values())
98
99
    def u(s, errors='replace'):
100
        if isinstance(s, text_type):
101
            return s
102
        return s.decode('utf-8', errors=errors)
103
104
    def b(s, errors='replace'):
105
        if isinstance(s, binary_type):
106
            return s
107
        return s.encode('utf-8', errors=errors)
108
109
    def n(s):
110
        '''Only in Python 2...
111
        from future.utils import bytes_to_native_str as n
112
        '''
113
        return s
114
115
    def nativestr(s, errors='replace'):
116
        if isinstance(s, text_type):
117
            return s
118
        elif isinstance(s, (int, float)):
119
            return s.__str__()
120
        else:
121
            return s.decode('utf-8', errors=errors)
122
123
    def system_exec(command):
124
        """Execute a system command and return the result as a str"""
125
        try:
126
            res = subprocess.run(command.split(' '), stdout=subprocess.PIPE).stdout.decode('utf-8')
127
        except Exception as e:
128
            logger.debug('Can not evaluate command {} ({})'.format(command, e))
129
            res = ''
130
        return res.rstrip()
131
132
else:
133
    from future.utils import bytes_to_native_str as n
134
    import Queue as queue
135
    from itertools import imap as map
136
    from ConfigParser import SafeConfigParser as ConfigParser, NoOptionError, NoSectionError
137
    from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
138
    from xmlrpclib import Fault, ProtocolError, ServerProxy, Transport, Server
139
    from urllib2 import urlopen, HTTPError, URLError
140
    from urlparse import urlparse
141
142
    # Correct issue #1025 by monkey path the xmlrpc lib
143
    from defusedxml.xmlrpc import monkey_patch
144
145
    monkey_patch()
146
147
    input = raw_input
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable raw_input does not seem to be defined.
Loading history...
148
    range = xrange
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
149
    ConfigParser.read_file = ConfigParser.readfp
150
151
    text_type = unicode
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
152
    binary_type = str
153
    bool_type = types.BooleanType
154
    long = long
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable long does not seem to be defined.
Loading history...
155
156
    PermissionError = OSError
157
158
    viewkeys = operator.methodcaller('viewkeys')
159
    viewvalues = operator.methodcaller('viewvalues')
160
    viewitems = operator.methodcaller('viewitems')
161
162
    def printandflush(string):
163
        """Print and flush (used by stdout* outputs modules)"""
164
        print(string)
165
        sys.stdout.flush()
166
167
    def mean(numbers):
168
        return float(sum(numbers)) / max(len(numbers), 1)
169
170
    def to_ascii(s):
171
        """Convert the unicode 's' to a ASCII string
172
173
        Useful to remove accent (diacritics)
174
        """
175
        if isinstance(s, binary_type):
176
            return s
177
        return unicodedata.normalize('NFKD', s).encode('ascii', 'ignore')
178
179
    def listitems(d):
180
        return d.items()
181
182
    def listkeys(d):
183
        return d.keys()
184
185
    def listvalues(d):
186
        return d.values()
187
188
    def iteritems(d):
189
        return d.iteritems()
190
191
    def iterkeys(d):
192
        return d.iterkeys()
193
194
    def itervalues(d):
195
        return d.itervalues()
196
197
    def u(s, errors='replace'):
198
        if isinstance(s, text_type):
199
            return s.encode('utf-8', errors=errors)
200
        return s.decode('utf-8', errors=errors)
201
202
    def b(s, errors='replace'):
203
        if isinstance(s, binary_type):
204
            return s
205
        return s.encode('utf-8', errors=errors)
206
207
    def nativestr(s, errors='replace'):
208
        if isinstance(s, binary_type):
209
            return s
210
        elif isinstance(s, (int, float)):
211
            return s.__str__()
212
        else:
213
            return s.encode('utf-8', errors=errors)
214
215
    def system_exec(command):
216
        """Execute a system command and return the resul as a str"""
217
        try:
218
            res = subprocess.check_output(command.split(' '))
219
        except Exception as e:
220
            logger.debug('Can not execute command {} ({})'.format(command, e))
221
            res = ''
222
        return res.rstrip()
223
224
225
# Globals functions for both Python 2 and 3
226
227
228
def subsample(data, sampling):
229
    """Compute a simple mean subsampling.
230
231
    Data should be a list of numerical itervalues
232
233
    :return: a sub-sampled list of sampling length
234
    """
235
    if len(data) <= sampling:
236
        return data
237
    sampling_length = int(round(len(data) / float(sampling)))
238
    return [mean(data[s * sampling_length : (s + 1) * sampling_length]) for s in range(0, sampling)]
239
240
241
def time_serie_subsample(data, sampling):
242
    """Compute a simple mean subsampling.
243
244
    Data should be a list of set (time, value)
245
246
    :return: a sub-sampled list of sampling length
247
    """
248
    if len(data) <= sampling:
249
        return data
250
    t = [t[0] for t in data]
251
    v = [t[1] for t in data]
252
    sampling_length = int(round(len(data) / float(sampling)))
253
    t_sub_sampled = [t[s * sampling_length : (s + 1) * sampling_length][0] for s in range(0, sampling)]
254
    v_sub_sampled = [mean(v[s * sampling_length : (s + 1) * sampling_length]) for s in range(0, sampling)]
255
    return list(zip(t_sub_sampled, v_sub_sampled))
256
257
258
def to_fahrenheit(celsius):
259
    """Convert Celsius to Fahrenheit."""
260
    return celsius * 1.8 + 32
261
262
263
def is_admin():
264
    """Return if current user is an admin or not
265
266
    The inner function fails unless you have Windows XP SP2 or higher.
267
    The failure causes a traceback to be printed and this function to return False.
268
269
    https://stackoverflow.com/a/19719292
270
271
    :return: True if the current user is an 'Admin' whatever that means (root on Unix), otherwise False.
272
    """
273
274
    if os.name == 'nt':
275
        import ctypes
276
        import traceback
277
278
        # WARNING: requires Windows XP SP2 or higher!
279
        try:
280
            return ctypes.windll.shell32.IsUserAnAdmin()
281
        except Exception as e:
282
            traceback.print_exc()
283
            return False
284
    else:
285
        # Check for root on Posix
286
        return os.getuid() == 0
287
288
289
def key_exist_value_not_none(k, d):
290
    # Return True if:
291
    # - key k exists
292
    # - d[k] is not None
293
    return k in d and d[k] is not None
294
295
296
def key_exist_value_not_none_not_v(k, d, v=''):
297
    # Return True if:
298
    # - key k exists
299
    # - d[k] is not None
300
    # - d[k] != v
301
    return k in d and d[k] is not None and d[k] != v
302
303
304
def disable(class_name, var):
305
    """Set disable_<var> to True in the class class_name."""
306
    setattr(class_name, 'enable_' + var, False)
307
    setattr(class_name, 'disable_' + var, True)
308
309
310
def enable(class_name, var):
311
    """Set disable_<var> to False in the class class_name."""
312
    setattr(class_name, 'enable_' + var, True)
313
    setattr(class_name, 'disable_' + var, False)
314
315
316
def pretty_date(time=False):
317
    """
318
    Get a datetime object or a int() Epoch timestamp and return a
319
    pretty string like 'an hour ago', 'Yesterday', '3 months ago',
320
    'just now', etc
321
    Source: https://stackoverflow.com/questions/1551382/user-friendly-time-format-in-python
322
    """
323
    now = datetime.now()
324
    if type(time) is int:
325
        diff = now - datetime.fromtimestamp(time)
326
    elif isinstance(time, datetime):
327
        diff = now - time
328
    elif not time:
329
        diff = 0
330
    second_diff = diff.seconds
0 ignored issues
show
introduced by
The variable diff does not seem to be defined for all execution paths.
Loading history...
331
    day_diff = diff.days
332
333
    if day_diff < 0:
334
        return ''
335
336
    if day_diff == 0:
337
        if second_diff < 10:
338
            return "just now"
339
        if second_diff < 60:
340
            return str(second_diff) + " seconds"
341
        if second_diff < 120:
342
            return "a minute"
343
        if second_diff < 3600:
344
            return str(second_diff // 60) + " minutes"
345
        if second_diff < 7200:
346
            return "an hour"
347
        if second_diff < 86400:
348
            return str(second_diff // 3600) + " hours"
349
    if day_diff == 1:
350
        return "Yesterday"
351
    if day_diff < 7:
352
        return str(day_diff) + " days"
353
    if day_diff < 31:
354
        return str(day_diff // 7) + " weeks"
355
    if day_diff < 365:
356
        return str(day_diff // 30) + " months"
357
    return str(day_diff // 365) + " years"
358