reykit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reykit/__init__.py +41 -0
- reykit/rall.py +33 -0
- reykit/rcomm.py +431 -0
- reykit/rdata.py +395 -0
- reykit/rdll/__init__.py +17 -0
- reykit/rdll/rdll_inject.py +41 -0
- reykit/rdll/rdll_inject_core.py +202 -0
- reykit/remail.py +276 -0
- reykit/rexception.py +339 -0
- reykit/rimage.py +261 -0
- reykit/rlog.py +1061 -0
- reykit/rmonkey.py +341 -0
- reykit/rmultitask.py +871 -0
- reykit/rnumber.py +161 -0
- reykit/ros.py +1917 -0
- reykit/rrandom.py +351 -0
- reykit/rregex.py +293 -0
- reykit/rschedule.py +272 -0
- reykit/rstdout.py +356 -0
- reykit/rsystem.py +1180 -0
- reykit/rtable.py +511 -0
- reykit/rtext.py +458 -0
- reykit/rtime.py +678 -0
- reykit/rtype.py +106 -0
- reykit/rwrap.py +613 -0
- reykit/rzip.py +137 -0
- reykit-1.0.0.dist-info/METADATA +29 -0
- reykit-1.0.0.dist-info/RECORD +30 -0
- reykit-1.0.0.dist-info/WHEEL +5 -0
- reykit-1.0.0.dist-info/top_level.txt +1 -0
reykit/rtime.py
ADDED
@@ -0,0 +1,678 @@
|
|
1
|
+
# !/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
@Time : 2022-12-05 14:11:50
|
6
|
+
@Author : Rey
|
7
|
+
@Contact : reyxbo@163.com
|
8
|
+
@Explain : Time methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
from typing import Any, TypedDict, Literal, Optional, Union, overload, NoReturn
|
13
|
+
from collections.abc import Callable
|
14
|
+
from pandas import (
|
15
|
+
DataFrame,
|
16
|
+
Timestamp as pd_timestamp,
|
17
|
+
Timedelta as pd_timedelta
|
18
|
+
)
|
19
|
+
from time import (
|
20
|
+
struct_time as time_struct_time,
|
21
|
+
strftime as time_strftime,
|
22
|
+
time as time_time,
|
23
|
+
sleep as time_sleep
|
24
|
+
)
|
25
|
+
from datetime import (
|
26
|
+
datetime as datetime_datetime,
|
27
|
+
date as datetime_date,
|
28
|
+
time as datetime_time,
|
29
|
+
timedelta as datetime_timedelta
|
30
|
+
)
|
31
|
+
|
32
|
+
from .rexception import throw
|
33
|
+
from .rnumber import digits
|
34
|
+
from .rrandom import randn
|
35
|
+
from .rregex import search
|
36
|
+
from .rstdout import echo
|
37
|
+
|
38
|
+
|
39
|
+
__all__ = (
|
40
|
+
'now',
|
41
|
+
'time_to',
|
42
|
+
'text_to_time',
|
43
|
+
'to_time',
|
44
|
+
'sleep',
|
45
|
+
'wait',
|
46
|
+
'RTimeMark'
|
47
|
+
)
|
48
|
+
|
49
|
+
|
50
|
+
RecordData = TypedDict('RecordData', {'timestamp': int, 'datetime': datetime_datetime, 'timedelta': Optional[datetime_timedelta], 'note': Optional[str]})
|
51
|
+
|
52
|
+
|
53
|
+
@overload
|
54
|
+
def now(format_: Literal['datetime'] = 'datetime') -> datetime_datetime: ...
|
55
|
+
|
56
|
+
@overload
|
57
|
+
def now(format_: Literal['date'] = 'datetime') -> datetime_date: ...
|
58
|
+
|
59
|
+
@overload
|
60
|
+
def now(format_: Literal['time'] = 'datetime') -> datetime_time: ...
|
61
|
+
|
62
|
+
@overload
|
63
|
+
def now(format_: Literal['datetime_str', 'date_str', 'time_str'] = 'datetime') -> str: ...
|
64
|
+
|
65
|
+
@overload
|
66
|
+
def now(format_: Literal['timestamp'] = 'datetime') -> int: ...
|
67
|
+
|
68
|
+
@overload
|
69
|
+
def now(format_: Any) -> NoReturn: ...
|
70
|
+
|
71
|
+
def now(
|
72
|
+
format_: Literal[
|
73
|
+
'datetime',
|
74
|
+
'date',
|
75
|
+
'time',
|
76
|
+
'datetime_str',
|
77
|
+
'date_str',
|
78
|
+
'time_str',
|
79
|
+
'timestamp'
|
80
|
+
] = 'datetime'
|
81
|
+
) -> Union[
|
82
|
+
datetime_datetime,
|
83
|
+
datetime_date,
|
84
|
+
datetime_time,
|
85
|
+
str,
|
86
|
+
int
|
87
|
+
]:
|
88
|
+
"""
|
89
|
+
Get the now time.
|
90
|
+
|
91
|
+
Parameters
|
92
|
+
----------
|
93
|
+
format_ : Format type.
|
94
|
+
- `Literal['datetime']`: Return datetime object of datetime package.
|
95
|
+
- `Literal['date']`: Return date object of datetime package.
|
96
|
+
- `Literal['time']`: Return time object of datetime package.
|
97
|
+
- `Literal['datetime_str']`: Return string in format `'%Y-%m-%d %H:%M:%S'`.
|
98
|
+
- `Literal['date_str']`: Return string in format `'%Y-%m-%d'`.
|
99
|
+
- `Literal['time_str']`: Return string in foramt `'%H:%M:%S'`.
|
100
|
+
- `Literal['timestamp']`: Return time stamp in milliseconds.
|
101
|
+
|
102
|
+
Returns
|
103
|
+
-------
|
104
|
+
The now time.
|
105
|
+
"""
|
106
|
+
|
107
|
+
# Return.
|
108
|
+
match format_:
|
109
|
+
case 'datetime':
|
110
|
+
return datetime_datetime.now()
|
111
|
+
case 'date':
|
112
|
+
return datetime_datetime.now().date()
|
113
|
+
case 'time':
|
114
|
+
return datetime_datetime.now().time()
|
115
|
+
case 'datetime_str':
|
116
|
+
return datetime_datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
117
|
+
case 'date_str':
|
118
|
+
return datetime_datetime.now().strftime('%Y-%m-%d')
|
119
|
+
case 'time_str':
|
120
|
+
return datetime_datetime.now().strftime('%H:%M:%S')
|
121
|
+
case 'timestamp':
|
122
|
+
return int(time_time() * 1000)
|
123
|
+
case _:
|
124
|
+
throw(ValueError, format_)
|
125
|
+
|
126
|
+
|
127
|
+
@overload
|
128
|
+
def time_to(
|
129
|
+
obj: Union[
|
130
|
+
datetime_datetime,
|
131
|
+
datetime_date,
|
132
|
+
datetime_time,
|
133
|
+
datetime_timedelta,
|
134
|
+
time_struct_time,
|
135
|
+
pd_timestamp,
|
136
|
+
pd_timedelta
|
137
|
+
],
|
138
|
+
decimal: bool = False,
|
139
|
+
raising: bool = True
|
140
|
+
) -> str: ...
|
141
|
+
|
142
|
+
@overload
|
143
|
+
def time_to(
|
144
|
+
obj: Any,
|
145
|
+
decimal: bool = False,
|
146
|
+
raising: Literal[True] = True
|
147
|
+
) -> NoReturn: ...
|
148
|
+
|
149
|
+
@overload
|
150
|
+
def time_to(
|
151
|
+
obj: Any,
|
152
|
+
decimal: bool = False,
|
153
|
+
raising: Literal[False] = True
|
154
|
+
) -> Any: ...
|
155
|
+
|
156
|
+
def time_to(
|
157
|
+
obj: Any,
|
158
|
+
decimal: bool = False,
|
159
|
+
raising: bool = True
|
160
|
+
) -> Any:
|
161
|
+
"""
|
162
|
+
Convert time object to text.
|
163
|
+
|
164
|
+
Parameters
|
165
|
+
----------
|
166
|
+
obj : Time object.
|
167
|
+
- `datetime`: Text format is `'%Y-%m-%d %H:%M:%S'`.
|
168
|
+
- `date`: Text format is `'%Y-%m-%d'`.
|
169
|
+
- `time`: Text format is `'%H:%M:%S'`.
|
170
|
+
- `struct_time`: Text format is `'%Y-%m-%d %H:%M:%S'`.
|
171
|
+
decimal : Whether with decimal, precision to microseconds.
|
172
|
+
raising : When parameter `obj` value error, whether throw exception, otherwise return original value.
|
173
|
+
|
174
|
+
Returns
|
175
|
+
-------
|
176
|
+
Converted text.
|
177
|
+
"""
|
178
|
+
|
179
|
+
match obj:
|
180
|
+
|
181
|
+
# Type 'datetime'.
|
182
|
+
case datetime_datetime() | pd_timestamp():
|
183
|
+
if decimal:
|
184
|
+
format_ = '%Y-%m-%d %H:%M:%S.%f'
|
185
|
+
else:
|
186
|
+
format_ = '%Y-%m-%d %H:%M:%S'
|
187
|
+
text = obj.strftime(format_)
|
188
|
+
|
189
|
+
# Type 'date'.
|
190
|
+
case datetime_date():
|
191
|
+
text = obj.strftime('%Y-%m-%d')
|
192
|
+
|
193
|
+
# Type 'time'.
|
194
|
+
case datetime_time():
|
195
|
+
if decimal:
|
196
|
+
format_ = '%H:%M:%S.%f'
|
197
|
+
else:
|
198
|
+
format_ = '%H:%M:%S'
|
199
|
+
text = obj.strftime(format_)
|
200
|
+
|
201
|
+
# Type 'timedelta'.
|
202
|
+
case datetime_timedelta() | pd_timedelta():
|
203
|
+
timestamp = obj.seconds + obj.microseconds / 1000_000
|
204
|
+
if timestamp >= 0:
|
205
|
+
timestamp += 57600
|
206
|
+
time = datetime_datetime.fromtimestamp(timestamp).time()
|
207
|
+
if decimal:
|
208
|
+
format_ = '%H:%M:%S.%f'
|
209
|
+
else:
|
210
|
+
format_ = '%H:%M:%S'
|
211
|
+
text = time.strftime(format_)
|
212
|
+
if obj.days != 0:
|
213
|
+
text = f'{obj.days}day ' + text
|
214
|
+
|
215
|
+
## Throw exception.
|
216
|
+
elif raising:
|
217
|
+
throw(ValueError, obj)
|
218
|
+
|
219
|
+
## Not raise.
|
220
|
+
else:
|
221
|
+
return obj
|
222
|
+
|
223
|
+
# Type 'struct_time'.
|
224
|
+
case time_struct_time():
|
225
|
+
if decimal:
|
226
|
+
format_ = '%Y-%m-%d %H:%M:%S.%f'
|
227
|
+
else:
|
228
|
+
format_ = '%Y-%m-%d %H:%M:%S'
|
229
|
+
text = time_strftime(format_, obj)
|
230
|
+
|
231
|
+
# Throw exception.
|
232
|
+
case _ if raising:
|
233
|
+
throw(TypeError, obj)
|
234
|
+
|
235
|
+
# Not raise.
|
236
|
+
case _:
|
237
|
+
return obj
|
238
|
+
|
239
|
+
return text
|
240
|
+
|
241
|
+
|
242
|
+
def text_to_time(
|
243
|
+
string: str
|
244
|
+
) -> Optional[
|
245
|
+
Union[
|
246
|
+
datetime_datetime,
|
247
|
+
datetime_date,
|
248
|
+
datetime_time
|
249
|
+
]
|
250
|
+
]:
|
251
|
+
"""
|
252
|
+
Convert text to time object.
|
253
|
+
|
254
|
+
Parameters
|
255
|
+
----------
|
256
|
+
string : String.
|
257
|
+
|
258
|
+
Returns
|
259
|
+
-------
|
260
|
+
Object or null.
|
261
|
+
"""
|
262
|
+
|
263
|
+
# Get parameter.
|
264
|
+
time_obj = None
|
265
|
+
str_len = len(string)
|
266
|
+
|
267
|
+
# Extract.
|
268
|
+
|
269
|
+
## Standard.
|
270
|
+
if 14 <= str_len <= 19:
|
271
|
+
try:
|
272
|
+
time_obj = datetime_datetime.strptime(string, '%Y-%m-%d %H:%M:%S')
|
273
|
+
except ValueError:
|
274
|
+
pass
|
275
|
+
else:
|
276
|
+
return time_obj
|
277
|
+
if 8 <= str_len <= 10:
|
278
|
+
try:
|
279
|
+
time_obj = datetime_datetime.strptime(string, '%Y-%m-%d').date()
|
280
|
+
except ValueError:
|
281
|
+
pass
|
282
|
+
else:
|
283
|
+
return time_obj
|
284
|
+
if 5 <= str_len <= 8:
|
285
|
+
try:
|
286
|
+
time_obj = datetime_datetime.strptime(string, '%H:%M:%S').time()
|
287
|
+
except ValueError:
|
288
|
+
pass
|
289
|
+
else:
|
290
|
+
return time_obj
|
291
|
+
|
292
|
+
## Regular.
|
293
|
+
|
294
|
+
### Type 'datetime'.
|
295
|
+
if 14 <= str_len <= 21:
|
296
|
+
pattern = r'^(\d{4})\S(\d{1,2})\S(\d{1,2})\S?.(\d{1,2})\S(\d{1,2})\S(\d{1,2})\S?$'
|
297
|
+
result = search(pattern, string)
|
298
|
+
if result is not None:
|
299
|
+
year, month, day, hour, minute, second = [
|
300
|
+
int(value)
|
301
|
+
for value in result
|
302
|
+
]
|
303
|
+
time_obj = datetime_datetime(year, month, day, hour, minute, second)
|
304
|
+
return time_obj
|
305
|
+
|
306
|
+
### Type 'date'.
|
307
|
+
if 8 <= str_len <= 11:
|
308
|
+
pattern = r'^(\d{4})\S(\d{1,2})\S(\d{1,2})\S?$'
|
309
|
+
result = search(pattern, string)
|
310
|
+
if result is not None:
|
311
|
+
year, month, day = [
|
312
|
+
int(value)
|
313
|
+
for value in result
|
314
|
+
]
|
315
|
+
time_obj = datetime_date(year, month, day)
|
316
|
+
return time_obj
|
317
|
+
|
318
|
+
### Type 'time'.
|
319
|
+
if 5 <= str_len <= 9:
|
320
|
+
pattern = r'^(\d{1,2})\S(\d{1,2})\S(\d{1,2})\S?$'
|
321
|
+
result = search(pattern, string)
|
322
|
+
if result is not None:
|
323
|
+
hour, minute, second = [
|
324
|
+
int(value)
|
325
|
+
for value in result
|
326
|
+
]
|
327
|
+
time_obj = datetime_time(hour, minute, second)
|
328
|
+
return time_obj
|
329
|
+
|
330
|
+
|
331
|
+
@overload
|
332
|
+
def to_time(
|
333
|
+
obj: str,
|
334
|
+
raising: bool = True
|
335
|
+
) -> Union[datetime_datetime, datetime_date, datetime_time]: ...
|
336
|
+
|
337
|
+
@overload
|
338
|
+
def to_time(
|
339
|
+
obj: Union[time_struct_time, float],
|
340
|
+
raising: bool = True
|
341
|
+
) -> datetime_datetime: ...
|
342
|
+
|
343
|
+
@overload
|
344
|
+
def to_time(
|
345
|
+
obj: Any,
|
346
|
+
raising: Literal[True] = True
|
347
|
+
) -> NoReturn: ...
|
348
|
+
|
349
|
+
@overload
|
350
|
+
def to_time(
|
351
|
+
obj: Any,
|
352
|
+
raising: Literal[False] = True
|
353
|
+
) -> Any: ...
|
354
|
+
|
355
|
+
def to_time(
|
356
|
+
obj: Any,
|
357
|
+
raising: bool = True
|
358
|
+
) -> Any:
|
359
|
+
"""
|
360
|
+
Convert object to time object.
|
361
|
+
|
362
|
+
Parameters
|
363
|
+
----------
|
364
|
+
obj : Object.
|
365
|
+
raising : When parameter `obj` value error, whether throw exception, otherwise return original value.
|
366
|
+
|
367
|
+
Returns
|
368
|
+
-------
|
369
|
+
Time object.
|
370
|
+
"""
|
371
|
+
|
372
|
+
match obj:
|
373
|
+
|
374
|
+
# Type 'str'.
|
375
|
+
case str():
|
376
|
+
time_obj = text_to_time(obj)
|
377
|
+
|
378
|
+
# Type 'struct_time'.
|
379
|
+
case time_struct_time():
|
380
|
+
time_obj = datetime_datetime(
|
381
|
+
obj.tm_year,
|
382
|
+
obj.tm_mon,
|
383
|
+
obj.tm_mday,
|
384
|
+
obj.tm_hour,
|
385
|
+
obj.tm_min,
|
386
|
+
obj.tm_sec
|
387
|
+
)
|
388
|
+
|
389
|
+
# Type 'float'.
|
390
|
+
case int() | float():
|
391
|
+
int_len, _ = digits(obj)
|
392
|
+
match int_len:
|
393
|
+
case 10:
|
394
|
+
time_obj = datetime_datetime.fromtimestamp(obj)
|
395
|
+
case 13:
|
396
|
+
time_obj = datetime_datetime.fromtimestamp(obj / 1000)
|
397
|
+
case _:
|
398
|
+
time_obj = None
|
399
|
+
|
400
|
+
# No time object.
|
401
|
+
if time_obj is None:
|
402
|
+
|
403
|
+
## Throw exception.
|
404
|
+
if raising:
|
405
|
+
throw(ValueError, obj)
|
406
|
+
|
407
|
+
## Not raise.
|
408
|
+
else:
|
409
|
+
return obj
|
410
|
+
|
411
|
+
return time_obj
|
412
|
+
|
413
|
+
|
414
|
+
def sleep(
|
415
|
+
*thresholds: float,
|
416
|
+
precision: Optional[int] = None
|
417
|
+
) -> float:
|
418
|
+
"""
|
419
|
+
Sleep random seconds.
|
420
|
+
|
421
|
+
Parameters
|
422
|
+
----------
|
423
|
+
thresholds : Low and high thresholds of random range, range contains thresholds.
|
424
|
+
- When `length is 0`, then low and high thresholds is `0` and `10`.
|
425
|
+
- When `length is 1`, then sleep this value.
|
426
|
+
- When `length is 2`, then low and high thresholds is `thresholds[0]` and `thresholds[1]`.
|
427
|
+
|
428
|
+
precision : Precision of random range, that is maximum decimal digits of sleep seconds.
|
429
|
+
- `None`: Set to Maximum decimal digits of element of parameter `thresholds`.
|
430
|
+
- `int`: Set to this value.
|
431
|
+
|
432
|
+
Returns
|
433
|
+
-------
|
434
|
+
Random seconds.
|
435
|
+
- When parameters `precision` is `0`, then return int.
|
436
|
+
- When parameters `precision` is `greater than 0`, then return float.
|
437
|
+
"""
|
438
|
+
|
439
|
+
# Handle parameter.
|
440
|
+
if len(thresholds) == 1:
|
441
|
+
second = float(thresholds[0])
|
442
|
+
else:
|
443
|
+
second = randn(*thresholds, precision=precision)
|
444
|
+
|
445
|
+
# Sleep.
|
446
|
+
time_sleep(second)
|
447
|
+
|
448
|
+
return second
|
449
|
+
|
450
|
+
|
451
|
+
def wait(
|
452
|
+
func: Callable[..., bool],
|
453
|
+
*args: Any,
|
454
|
+
_interval: float = 1,
|
455
|
+
_timeout: Optional[float] = None,
|
456
|
+
**kwargs: Any
|
457
|
+
) -> float:
|
458
|
+
"""
|
459
|
+
Wait success, timeout throw exception.
|
460
|
+
|
461
|
+
Parameters
|
462
|
+
----------
|
463
|
+
func : Function to be decorated, must return `bool` value.
|
464
|
+
args : Position arguments of decorated function.
|
465
|
+
_interval : Interval seconds.
|
466
|
+
_timeout : Timeout seconds, timeout throw exception.
|
467
|
+
- `None`: Infinite time.
|
468
|
+
- `float`: Use this time.
|
469
|
+
kwargs : Keyword arguments of decorated function.
|
470
|
+
|
471
|
+
Returns
|
472
|
+
-------
|
473
|
+
Total spend seconds.
|
474
|
+
"""
|
475
|
+
|
476
|
+
# Set parameter.
|
477
|
+
rtm = RTimeMark()
|
478
|
+
rtm()
|
479
|
+
|
480
|
+
# Not set timeout.
|
481
|
+
if _timeout is None:
|
482
|
+
|
483
|
+
## Wait.
|
484
|
+
while True:
|
485
|
+
success = func(*args, **kwargs)
|
486
|
+
if success: break
|
487
|
+
sleep(_interval)
|
488
|
+
|
489
|
+
# Set timeout.
|
490
|
+
else:
|
491
|
+
|
492
|
+
## Wait.
|
493
|
+
while True:
|
494
|
+
success = func(*args, **kwargs)
|
495
|
+
if success: break
|
496
|
+
|
497
|
+
## Timeout.
|
498
|
+
rtm()
|
499
|
+
if rtm.total_spend > _timeout:
|
500
|
+
throw(TimeoutError, _timeout)
|
501
|
+
|
502
|
+
## Sleep.
|
503
|
+
sleep(_interval)
|
504
|
+
|
505
|
+
## Return.
|
506
|
+
rtm()
|
507
|
+
return rtm.total_spend
|
508
|
+
|
509
|
+
|
510
|
+
class RTimeMark():
|
511
|
+
"""
|
512
|
+
Rey`s `time mark` type.
|
513
|
+
"""
|
514
|
+
|
515
|
+
|
516
|
+
def __init__(self) -> None:
|
517
|
+
"""
|
518
|
+
Build `time mark` attributes.
|
519
|
+
"""
|
520
|
+
|
521
|
+
# Record table.
|
522
|
+
self.record: dict[int, RecordData] = {}
|
523
|
+
|
524
|
+
|
525
|
+
def mark(self, note: Optional[str] = None) -> int:
|
526
|
+
"""
|
527
|
+
Marking now time.
|
528
|
+
|
529
|
+
Parameters
|
530
|
+
----------
|
531
|
+
note : Mark note.
|
532
|
+
|
533
|
+
Returns
|
534
|
+
-------
|
535
|
+
Mark index.
|
536
|
+
"""
|
537
|
+
|
538
|
+
# Get parametes.
|
539
|
+
|
540
|
+
# Mark.
|
541
|
+
index = len(self.record)
|
542
|
+
now_timestamp = now('timestamp')
|
543
|
+
now_datetime = now('datetime')
|
544
|
+
record = {
|
545
|
+
'timestamp': now_timestamp,
|
546
|
+
'datetime': now_datetime,
|
547
|
+
'timedelta': None,
|
548
|
+
'note': note
|
549
|
+
}
|
550
|
+
|
551
|
+
## Not first.
|
552
|
+
if index != 0:
|
553
|
+
last_index = index - 1
|
554
|
+
last_datetime = self.record[last_index]['datetime']
|
555
|
+
record['timedelta'] = now_datetime - last_datetime
|
556
|
+
|
557
|
+
## Record.
|
558
|
+
self.record[index] = record
|
559
|
+
|
560
|
+
return index
|
561
|
+
|
562
|
+
|
563
|
+
def report(self, title: Optional[str] = None) -> DataFrame:
|
564
|
+
"""
|
565
|
+
Print and return time mark information table.
|
566
|
+
|
567
|
+
Parameters
|
568
|
+
----------
|
569
|
+
title : Print title.
|
570
|
+
- `None`: Not print.
|
571
|
+
- `str`: Print and use this title.
|
572
|
+
|
573
|
+
Returns
|
574
|
+
-------
|
575
|
+
Time mark information table
|
576
|
+
"""
|
577
|
+
|
578
|
+
# Get parameter.
|
579
|
+
record_len = len(self.record)
|
580
|
+
data = [
|
581
|
+
info.copy()
|
582
|
+
for info in self.record.values()
|
583
|
+
]
|
584
|
+
indexes = [
|
585
|
+
index
|
586
|
+
for index in self.record
|
587
|
+
]
|
588
|
+
|
589
|
+
# Generate report.
|
590
|
+
|
591
|
+
## No record.
|
592
|
+
if record_len == 0:
|
593
|
+
row: RecordData = dict.fromkeys(('timestamp', 'datetime', 'timedelta', 'note'))
|
594
|
+
data = [row]
|
595
|
+
indexes = [0]
|
596
|
+
|
597
|
+
## Add total row.
|
598
|
+
if record_len > 2:
|
599
|
+
row: RecordData = dict.fromkeys(('timestamp', 'datetime', 'timedelta', 'note'))
|
600
|
+
max_index = record_len - 1
|
601
|
+
total_timedelta = self.record[max_index]['datetime'] - self.record[0]['datetime']
|
602
|
+
row['timedelta'] = total_timedelta
|
603
|
+
data.append(row)
|
604
|
+
indexes.append('total')
|
605
|
+
|
606
|
+
## Convert.
|
607
|
+
for row in data:
|
608
|
+
if row['timestamp'] is not None:
|
609
|
+
row['timestamp'] = str(row['timestamp'])
|
610
|
+
if row['datetime'] is not None:
|
611
|
+
row['datetime'] = str(row['datetime'])[:-3]
|
612
|
+
if row['timedelta'] is not None:
|
613
|
+
if row['timedelta'].total_seconds() == 0:
|
614
|
+
timedelta_str = '00:00:00.000'
|
615
|
+
else:
|
616
|
+
timedelta_str = str(row['timedelta'])[:-3]
|
617
|
+
timedelta_str = timedelta_str.rsplit(' ', 1)[-1]
|
618
|
+
if timedelta_str[1] == ':':
|
619
|
+
timedelta_str = '0' + timedelta_str
|
620
|
+
if row['timedelta'].days != 0:
|
621
|
+
timedelta_str = '%sday %s' % (
|
622
|
+
row['timedelta'].days,
|
623
|
+
timedelta_str
|
624
|
+
)
|
625
|
+
row['timedelta'] = timedelta_str
|
626
|
+
df_info = DataFrame(data, index=indexes)
|
627
|
+
df_info.fillna('-', inplace=True)
|
628
|
+
|
629
|
+
# Print.
|
630
|
+
if title is not None:
|
631
|
+
echo(df_info, title=title)
|
632
|
+
|
633
|
+
return df_info
|
634
|
+
|
635
|
+
|
636
|
+
@property
|
637
|
+
def total_spend(self) -> float:
|
638
|
+
"""
|
639
|
+
Get total spend seconds.
|
640
|
+
|
641
|
+
Returns
|
642
|
+
-------
|
643
|
+
Total spend seconds.
|
644
|
+
"""
|
645
|
+
|
646
|
+
# Break.
|
647
|
+
if len(self.record) <= 1: return 0.0
|
648
|
+
|
649
|
+
# Get parameter.
|
650
|
+
first_timestamp = self.record[0]['timestamp']
|
651
|
+
max_index = max(self.record)
|
652
|
+
last_timestamp = self.record[max_index]['timestamp']
|
653
|
+
|
654
|
+
# Calculate.
|
655
|
+
seconds = round((last_timestamp - first_timestamp) / 1000, 3)
|
656
|
+
|
657
|
+
return seconds
|
658
|
+
|
659
|
+
|
660
|
+
def __str__(self) -> str:
|
661
|
+
"""
|
662
|
+
Convert to string.
|
663
|
+
|
664
|
+
Returns
|
665
|
+
-------
|
666
|
+
Converted string.
|
667
|
+
"""
|
668
|
+
|
669
|
+
# Get.
|
670
|
+
report = self.report()
|
671
|
+
|
672
|
+
# Convert.
|
673
|
+
string = str(report)
|
674
|
+
|
675
|
+
return string
|
676
|
+
|
677
|
+
|
678
|
+
__call__ = mark
|