forked from timvieira/arsenal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
humanreadable.py
261 lines (222 loc) · 7.65 KB
/
humanreadable.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
from datetime import datetime, timedelta
from time import clock, localtime, strftime
import atexit
# TODO: option to use terminal width if possible
def marquee(txt='', width=78, mark='*'):
"""
Return the input string centered in a 'marquee'.
>>> marquee('hello', width=50)
'********************* hello *********************'
"""
if not txt:
return (mark*width)[:width]
nmark = (width-len(txt)-2)/len(mark)/2
if nmark < 0: nmark =0
marks = mark*nmark
return '%s %s %s' % (marks, txt, marks)
def nth(n):
"""
Formats an ordinal.
Doesn't handle negative numbers.
>>> nth(1)
'1st'
>>> nth(0)
'0th'
>>> [nth(x) for x in [2, 3, 4, 5, 10, 11, 12, 13, 14]]
['2nd', '3rd', '4th', '5th', '10th', '11th', '12th', '13th', '14th']
>>> [nth(x) for x in [91, 92, 93, 94, 99, 100, 101, 102]]
['91st', '92nd', '93rd', '94th', '99th', '100th', '101st', '102nd']
>>> [nth(x) for x in [111, 112, 113, 114, 115]]
['111th', '112th', '113th', '114th', '115th']
"""
assert n >= 0
if n % 100 in [11, 12, 13]:
return '%sth' % n
return {1: '%sst', 2: '%snd', 3: '%srd'}.get(n % 10, '%sth') % n
def timetuple(s):
"""htime(x) -> (days, hours, minutes, seconds)"""
s = int(s)
d, s = divmod(s, 86400)
h, s = divmod(s, 3600)
m, s = divmod(s, 60)
return (d, h, m, s)
def htime(s, show_seconds=True):
"""Given a number of seconds, returns a string attempting to represent
it as shortly as possible.
>>> htime(100000)
'1d3h46m40s'
>>> htime(1)
'1s'
>>> htime(10)
'10s'
>>> htime(1000)
'16m40s'
>>> htime(10000)
'2h46m40s'
"""
(d, h, m, s) = timetuple(s)
x = []
if d:
x.append('%sd' % d)
if h:
x.append('%sh' % h)
if m:
x.append('%sm' % m)
if show_seconds and s:
x.append('%ss' % s)
if not x:
if show_seconds:
x = ['%ss' % s]
else:
x = ['0m']
return ''.join(x)
def datestr(then, now=None):
"""
Converts a (UTC) datetime object to a nice string representation.
>>> d = datetime(1970, 5, 1)
>>> datestr(d, now=d)
'0 microseconds ago'
>>> datestr(datetime(1970, 1, 1), now=d)
'January 1'
>>> datestr(datetime(1969, 1, 1), now=d)
'January 1, 1969'
>>> datestr(datetime(1970, 6, 1), now=d)
'June 1, 1970'
>>> datestr(None)
''
"""
def agohence(n, what, divisor=None):
if divisor:
n //= divisor
out = str(abs(n)) + ' ' + what # '2 day'
if abs(n) != 1:
out += 's' # '2 days'
out += ' ' # '2 days '
if n < 0:
out += 'from now'
else:
out += 'ago'
return out # '2 days ago'
oneday = 24 * 60 * 60
if not then:
return ""
if not now:
now = datetime.utcnow()
if type(now).__name__ == "DateTime":
now = datetime.fromtimestamp(now)
if type(then).__name__ == "DateTime":
then = datetime.fromtimestamp(then)
elif type(then).__name__ == "date":
then = datetime(then.year, then.month, then.day)
delta = now - then
deltaseconds = int(delta.days * oneday + delta.seconds
+ delta.microseconds * 1e-06)
deltadays = abs(deltaseconds) // oneday
if deltaseconds < 0:
deltadays *= -1 # fix for oddity of floor
if deltadays:
if abs(deltadays) < 4:
return agohence(deltadays, 'day')
out = then.strftime('%B %e') # e.g. 'June 13'
if then.year != now.year or deltadays < 0:
out += ', %s' % then.year
return out
if int(deltaseconds):
if abs(deltaseconds) > (60 * 60):
return agohence(deltaseconds, 'hour', 60 * 60)
elif abs(deltaseconds) > 60:
return agohence(deltaseconds, 'minute', 60)
else:
return agohence(deltaseconds, 'second')
deltamicroseconds = delta.microseconds
if delta.days:
deltamicroseconds = int(delta.microseconds - 1e6) # datetime oddity
if abs(deltamicroseconds) > 1000:
return agohence(deltamicroseconds, 'millisecond', 1000)
return agohence(deltamicroseconds, 'microsecond')
# TODO: use htime and marquee
def print_elapsed_time():
"register an exit hook which prints the start, finish, and elapsed times of a script."
begin = clock()
started = localtime()
def hook():
secs = clock() - begin
mins, secs = divmod(secs, 60)
hrs, mins = divmod(mins, 60)
print
print '======================================================================'
print 'Started on', strftime("%B %d, %Y at %I:%M:%S %p", started)
print 'Finished on', strftime("%B %d, %Y at %I:%M:%S %p", localtime())
print 'Total time: %02d:%02d:%02d' % (hrs, mins, secs)
print
atexit.register(hook)
if __name__ == '__main__':
import doctest
doctest.testmod()
def test():
d = datetime(1970, 5, 1)
examples = {
timedelta(microseconds=1): '1 microsecond ago',
timedelta(microseconds=2): '2 microseconds ago',
-timedelta(microseconds=1): '1 microsecond from now',
-timedelta(microseconds=2): '2 microseconds from now',
timedelta(microseconds=2000): '2 milliseconds ago',
timedelta(seconds=2): '2 seconds ago',
timedelta(seconds=2*60): '2 minutes ago',
timedelta(seconds=2*60*60): '2 hours ago',
timedelta(days=2): '2 days ago',
}
for t, v in examples.items():
assert datestr(d, now=d+t) == v
test()
# borrowed from girzzled https://github.com/bmc/grizzled-python/blob/master/grizzled/text/__init__.py
def str2bool(s):
"""
Convert a string to a boolean value. The supported conversions are:
+--------------+---------------+
| String | Boolean value |
+==============+===============+
| "false" | False |
+--------------+---------------+
| "true" | True |
+--------------+---------------+
| "f" | False |
+--------------+---------------+
| "t" + True |
+--------------+---------------+
| "0" | False |
+--------------+---------------+
| "1" + True |
+--------------+---------------+
| "n" | False |
+--------------+---------------+
| "y" + True |
+--------------+---------------+
| "no" | False |
+--------------+---------------+
| "yes" + True |
+--------------+---------------+
| "off" | False |
+--------------+---------------+
| "on" + True |
+--------------+---------------+
Strings are compared in a case-blind fashion.
**Note**: This function is not currently localizable.
:Parameters:
s : str
The string to convert to boolean
:rtype: bool
:return: the corresponding boolean value
:raise ValueError: unrecognized boolean string
"""
try:
return {
'true' : True, 'false': False,
't' : True, 'f' : False,
'1' : True, '0' : False,
'yes' : True, 'no' : False,
'y' : True, 'n' : False,
'on' : True, 'off' : False,
}[s.lower()]
except KeyError:
raise ValueError, 'Unrecognized boolean string: "%s"' % s