#!/usr/bin/env python3 # Copyright 2017 vg@devys.org # SPDX-License-Identifier: MIT ''' Simple and fancy sleep for console. Time can be precised as XX[h|m|s] [YY[m|s]] [ZZ[s]] or XX:YY[:ZZ]. Usage: fancy-sleep [options] TIME... fancy-sleep -h|--help Options: -h, --help Display this help message ''' import contextlib import datetime import sys import time import re import docopt # to avoid accidental trailing spaces removal, use another character and # replace. NUMBERS = [i.replace('.', ' ') for i in ( """\ ██████ ██ ██ ██ ██ ██ ██ ██████ """, """\ ██ ██ ██ ██ ██ """, """\ ██████ ██ ██████ ██.... ██████ """, """\ ██████ ██ ██████ ██ ██████ """, """\ ██ ██ ██ ██ ██████ ██ ██ """, """\ ██████ ██.... ██████ ██ ██████ """, """\ ██████ ██.... ██████ ██ ██ ██████ """, """\ ██████ ██ ██ ██ ██ """, """\ ██████ ██ ██ ██████ ██ ██ ██████ """, """\ ██████ ██ ██ ██████ ██ ██████ """, """\ . ██. . ██. . """, )] def print_translated(string): 'print 09:30 style time to big:numbers' translated = [] for character in string: index = 0 if character >= '0' and character <= '9': index = ord(character) - ord('0') elif character == ':': index = 10 translated.append(NUMBERS[index]) display_lines = ['', '', '', '', '', ''] for index in range(5): for number in translated: display_lines[index] += number.splitlines()[index] print('\n'.join(display_lines), end='') def determine_delta(elements, time_pattern=re.compile(r'^(\d+)([dhms]?)$')): 'return a cumulative datetime.timedelta from elements list of strings' default = datetime.datetime.strptime('0', '%S') timedelta = datetime.timedelta() for element in elements: groups = time_pattern.match(element) if groups: if groups.group(2) == 'd': timedelta += datetime.timedelta(days=float(groups.group(1))) elif groups.group(2) == 'h': timedelta += datetime.timedelta(hours=float(groups.group(1))) elif groups.group(2) == 'm': timedelta += datetime.timedelta(minutes=float(groups.group(1))) elif groups.group(2) == 's' or groups.group(2) == '': timedelta += datetime.timedelta(seconds=float(groups.group(1))) #with contextlib.suppress(ValueError): # timedelta += datetime.datetime.strptime(element, '%dd') - default #with contextlib.suppress(ValueError): # timedelta += datetime.datetime.strptime(element, '%Hh') - default #with contextlib.suppress(ValueError): # timedelta += datetime.datetime.strptime(element, '%Mm') - default #with contextlib.suppress(ValueError): # timedelta += datetime.datetime.strptime(element, '%Ss') - default #with contextlib.suppress(ValueError): # timedelta += datetime.datetime.strptime(element, '%S') - default else: with contextlib.suppress(ValueError): timedelta += datetime.datetime.strptime(element, '%H:%M') - default with contextlib.suppress(ValueError): timedelta += datetime.datetime.strptime(element, '%H:%M:%S') - default return timedelta def test_determine_delta(): 'test determine_delta function (callable with pytest-3)' assert determine_delta('11'.split()) \ == datetime.timedelta(seconds=11) assert determine_delta('11s'.split()) \ == datetime.timedelta(seconds=11) assert determine_delta('11h 47m'.split()) \ == datetime.timedelta(hours=11, minutes=47) assert determine_delta('11h 47'.split()) \ == datetime.timedelta(hours=11, seconds=47) assert determine_delta('11h 47m 09'.split()) \ == datetime.timedelta(hours=11, minutes=47, seconds=9) assert determine_delta('09:53'.split()) \ == datetime.timedelta(hours=9, minutes=53) assert determine_delta('8:2 9'.split()) \ == datetime.timedelta(hours=8, minutes=2, seconds=9) assert determine_delta('8d 5:32 6s'.split()) \ == datetime.timedelta(days=8, hours=5, minutes=32, seconds=6) def main(): 'function called only when script invoked directly on command line' args = docopt.docopt(__doc__) curtime = datetime.datetime.now() timedelta = determine_delta(args['TIME']) target_time = curtime + timedelta print('Launched at:', curtime.strftime('%F %T')) print('Run until:', target_time.strftime('%F %T')) sys.stdout.flush() if sys.stdout.isatty(): print() print('\n'*5, '\x1b[5A', sep='', end='') print('\x1b[s', end='') # save cursor try: while True: delta = target_time.timestamp() - time.time() if delta <= 0: break hours = int(delta / 3600) minutes = int(delta / 60) % 60 seconds = int(delta) % 60 print('\x1b[u', end='') # restore cursor time_string = '%02d:%02d:%02d' % (hours, minutes, seconds) #print('\rremainining time: %s' % time_string, end='') print_translated(time_string) time.sleep(1) except KeyboardInterrupt: sys.exit(130) finally: print() else: try: time.sleep(target_time.timestamp() - time.time()) except KeyboardInterrupt: sys.exit(130) if __name__ == '__main__': main()