Completed
Push — master ( 1a2e8a...8f6c6f )
by Dieter
01:13
created

buildtimetrend.split_datetime()   A

Complexity

Conditions 3

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 3
dl 0
loc 51
rs 9.4109

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
# vim: set expandtab sw=4 ts=4:
2
"""
3
Collection of supporting functions.
4
5
Copyright (C) 2014-2016 Dieter Adriaenssens <[email protected]>
6
7
This file is part of buildtimetrend/python-lib
8
<https://github.com/buildtimetrend/python-lib/>
9
10
This program is free software: you can redistribute it and/or modify
11
it under the terms of the GNU Affero General Public License as published by
12
the Free Software Foundation, either version 3 of the License, or
13
any later version.
14
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU Affero General Public License for more details.
19
20
You should have received a copy of the GNU Affero General Public License
21
along with this program. If not, see <http://www.gnu.org/licenses/>.
22
"""
23
24
from __future__ import division
25
from builtins import str
26
from six import string_types
27
import os
28
from buildtimetrend import logger
29
from datetime import datetime
30
from dateutil.parser import parse
31
from dateutil.tz import tzutc
32
from decimal import Decimal
33
from decimal import getcontext
34
from numbers import Number
35
36
37
def format_timestamp(timestamp):
38
    """
39
    Format a datetime timestamp (UTC) to ISO format (YYYY-MM-DDTHH:MM:SS).
40
41
    Parameters :
42
    - timestamp : timestamp, seconds since epoch
43
    """
44
    timestamp_datetime = datetime.utcfromtimestamp(timestamp)
45
    return timestamp_datetime.isoformat()
46
47
48
def split_timestamp(timestamp):
49
    """
50
    Split a timestamp in seperate components.
51
52
    Split a timestamp (expressed in seconds since epoch)
53
    in all seperate components :
54
      year, month, day of month, day of week,
55
      hour (12 and 24 hour), minute, second
56
57
    Parameters :
58
    - timestamp : timestamp, seconds since epoch
59
    """
60
    if not isinstance(timestamp, Number):
61
        raise TypeError(
62
            "param timestamp should be a number {0!s}".format(type(timestamp))
63
        )
64
65
    ts_seconds = int(timestamp)
66
    ts_microseconds = int((timestamp - ts_seconds) * 1000000)
67
    dt_utc = datetime.utcfromtimestamp(ts_seconds).replace(
68
        tzinfo=tzutc(), microsecond=ts_microseconds
69
    )
70
    return split_datetime(dt_utc)
71
72
73
def split_isotimestamp(isotimestamp):
74
    """
75
    Split a ISO formatted timestamp in seperate components.
76
77
    Split a timestamp (in ISO format)
78
    in all seperate components :
79
      year, month, day of month, day of week,
80
      hour (12 and 24 hour), minute, second
81
82
    Parameters :
83
    - isotimestamp : timestamp in ISO format YYYY-MM-DDTHH:MM:SS
84
    """
85
    if is_string(isotimestamp, "isotimestamp"):
86
        # use dateutil.parser.parse to parse the timestamp
87
        return split_datetime(parse(isotimestamp, tzinfos={"UTC": +0}))
88
89
90
def split_datetime(timestamp_datetime):
91
    """
92
    Split a datetime timestamp in seperate components.
93
94
    Split a timestamp (is a datetime class instance)
95
    in all seperate components :
96
      year, month, day of month, day of week,
97
      hour (12 and 24 hour), minute, second
98
99
    Parameters :
100
101
    - timestamp_datetime : timestamp in datetime class format
102
    """
103
    if not isinstance(timestamp_datetime, datetime):
104
        raise TypeError(
105
            "param {0!s} should be a datetime instance".format(
106
                'timestamp_datetime'
107
            )
108
        )
109
110
    timestamp_dict = {}
111
    timestamp_dict["isotimestamp"] = timestamp_datetime.isoformat()
112
113
    # epoch = 1 Jan 1970
114
    epoch = datetime.utcfromtimestamp(0)
115
    # add timezone info to epoch if timestamp is timezone aware
116
    if timestamp_datetime.tzname() is not None:
117
        epoch = datetime.utcfromtimestamp(0).replace(tzinfo=tzutc())
118
119
    # seconds since epoch
120
    timestamp_dict["timestamp_seconds"] = \
121
        (timestamp_datetime - epoch).total_seconds()
122
123
    timestamp_dict["year"] = timestamp_datetime.strftime("%Y")
124
    timestamp_dict["month"] = timestamp_datetime.strftime("%m")
125
    timestamp_dict["month_short_en"] = timestamp_datetime.strftime("%b")
126
    timestamp_dict["month_full_en"] = timestamp_datetime.strftime("%B")
127
    timestamp_dict["day_of_month"] = timestamp_datetime.strftime("%d")
128
    timestamp_dict["day_of_week"] = timestamp_datetime.strftime("%w")
129
    timestamp_dict["day_of_week_short_en"] = timestamp_datetime.strftime("%a")
130
    timestamp_dict["day_of_week_full_en"] = timestamp_datetime.strftime("%A")
131
    timestamp_dict["hour_12"] = timestamp_datetime.strftime("%I")
132
    timestamp_dict["hour_ampm"] = timestamp_datetime.strftime("%p")
133
    timestamp_dict["hour_24"] = timestamp_datetime.strftime("%H")
134
    timestamp_dict["minute"] = timestamp_datetime.strftime("%M")
135
    timestamp_dict["second"] = timestamp_datetime.strftime("%S")
136
    timestamp_dict["microsecond"] = timestamp_datetime.strftime("%f")
137
    timestamp_dict["timezone"] = timestamp_datetime.strftime("%Z")
138
    timestamp_dict["timezone_offset"] = timestamp_datetime.strftime("%z")
139
140
    return timestamp_dict
141
142
143
def nano2sec(time):
144
    """
145
    Convert time from nanoseconds to seconds.
146
147
    Parameters:
148
    - time : time in nanoseconds
149
    """
150
    getcontext().prec = 20
151
    return Decimal(int(time)) / Decimal(1000000000)
152
153
154
def check_file(filename):
155
    """
156
    Check if a file exists.
157
158
    Parameters :
159
    - filename : file to be checked
160
    Return false if file doesn't exist, true if it exists.
161
    """
162
    # load timestamps file
163
    if not os.path.isfile(filename):
164
        logger.critical('File doesn\'t exist : %s', filename)
165
        return False
166
167
    return True
168
169
170
def file_is_newer(path1, path2):
171
    """
172
    Check if a file is newer than another file.
173
174
    Parameters :
175
    - path1 : path of first file
176
    - path2 : ipath of second file
177
    Return true if the first file is newer than the second one,
178
    return false if it is older, or if any of the files doesn't exist.
179
    """
180
    # check if files exist
181
    if not check_file(path1) or not check_file(path2):
182
        return False
183
184
    mtime1 = os.path.getmtime(path1)
185
    mtime2 = os.path.getmtime(path2)
186
187
    # check modification times
188
    return (mtime1 - mtime2) > 0
189
190
191
def is_dict(param_dict):
192
    """
193
    Return true if a parameter is a dictionary.
194
195
    Parameters :
196
    - param_dict: parameter that should be a dictonary
197
    Return true if parameter is a dictionary.
198
    """
199
    return param_dict is not None and type(param_dict) is dict
200
201
202
def check_dict(param_dict, name=None, key_list=None):
203
    """
204
    Check if a parameter is a dictionary.
205
206
    Returns True if it is a dictionary, false if it isn't,
207
    or if parameter 'name' is specified,
208
    an Error is raised with the name in the message.
209
210
    If key_list is defined,
211
    true is returned if the keys in key_list exits in the dictionary,
212
    false if they don't all exist.
213
214
    Parameters :
215
    - param_dict: parameter that should be a dictonary
216
    - name: name of the parameter
217
    - key_list: list of keys that should be present in the dict
218
    Return true if parameter is a dictionary, throws error when it isn't
219
    """
220
    if not is_dict(param_dict):
221
        if name is None:
222
            return False
223
        else:
224
            raise TypeError("param {0!s} should be a dictionary".format(name))
225
226
    # check if key_list is defined
227
    if key_list is None:
228
        # key_list is not defined, no need to check keys
229
        return True
230
    else:
231
        # check if dictionary contains all keys in key_list
232
        return keys_in_dict(param_dict, key_list)
233
234
235
def keys_in_dict(param_dict, key_list):
236
    """
237
    Check if a list of keys exist in a dictionary.
238
239
    Parameters :
240
    - param_dict: dictonary that should contain the keys
241
    - key_list: key or list of keys that should be present in the dict
242
    Return true if all keys were found in the dictionary
243
    """
244
    if isinstance(key_list, (string_types, int)):
245
        return key_list in param_dict
246
    elif not is_list(key_list):
247
        return False
248
249
    for key in key_list:
250
        if key not in param_dict:
251
            return False
252
253
    return True
254
255
256
def is_list(param_list, name=None):
257
    """
258
    Check if a parameter is a list.
259
260
    Returns True if parameter is a list, false if it isn't,
261
    or if parameter 'name' is specified,
262
    an Error is raised with the name of the parameter in the message.
263
264
    Parameters :
265
    - param_list: parameter that should be a list
266
    - name: name of the parameter
267
    """
268
    if param_list is None or type(param_list) is not list:
269
        if name is None:
270
            return False
271
        else:
272
            raise TypeError("param {0!s} should be a list".format(name))
273
274
    return True
275
276
277
def is_string(param, name=None):
278
    """
279
    Check if a parameter is a string.
280
281
    Returns True if parameter is a string, false if it isn't,
282
    If parameter 'name' is specified,
283
    an Error is raised with the name of the parameter in the message.
284
285
    Parameters :
286
    - param: parameter that should be a string
287
    - name: name of the parameter
288
    """
289
    if param is None or not isinstance(param, string_types):
290
        if name is None:
291
            return False
292
        else:
293
            raise TypeError("param {0!s} should be a string".format(name))
294
295
    return True
296
297
298
def check_num_string(num_string, name):
299
    """
300
    Check if a parameter is an integer or numerical string.
301
302
    Parameters :
303
    - num_string: parameter that should be a numerical string
304
    - name: name of the parameter
305
    Return integer of numerical string, throws error when it isn't
306
    """
307
    if num_string is None or not isinstance(num_string, (string_types, int)):
308
        err_msg = "param {0!s} should be a numerical string or an integer"
309
        raise TypeError(err_msg.format(name))
310
311
    return int(num_string)
312
313
314
def get_repo_slug(repo_owner=None, repo_name=None):
315
    """
316
    Return repo slug.
317
318
    This function concatenates repo_owner and repo_name,
319
    fe. buildtimetrend/service
320
321
    Parameters :
322
    - repo_owner : name of the Github repo owner, fe. `buildtimetrend`
323
    - repo_name : name of the Github repo, fe. `service`
324
    """
325
    if repo_owner is not None and repo_name is not None:
326
        return "{0!s}/{1!s}".format(str(repo_owner), str(repo_name))
327
    else:
328
        return None
329