-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpomy.py
executable file
·222 lines (180 loc) · 6.69 KB
/
pomy.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
#!/usr/bin/python3
from time import sleep
from datetime import datetime
from sys import argv
import platform
from subprocess import run
from shutil import which
import threading
minute_seconds = 60 if "--test" not in argv else 1
global_cycle_count = 1
set_count = 1
pomodoro_count = 0
pomodoro_duration = 25 * minute_seconds if "--test" not in argv else 1
short_break_duration = 5 * minute_seconds if "--test" not in argv else 1
long_break_duration = 15 * minute_seconds if "--test" not in argv else 1
operating_system = platform.system()
sound_binary = None
pomodoro_sfx = "sfx/pomodoro_sfx.wav"
break_sfx = "sfx/break_sfx.wav"
MAX_TIMER_CHARACTERS = 32
# Text format and colors
if "--no-color" in argv:
ORANGE = ""
SKY_BLUE = ""
YELLOW = ""
else:
from text_format import Colors
ORANGE = Colors.ORANGE
SKY_BLUE = Colors.SKY_BLUE
YELLOW = Colors.YELLOW
if "--no-format" in argv:
BOLD = ""
UNDERLINE = ""
RESET_FORMAT = ""
else:
from text_format import Format
BOLD = Format.BOLD
UNDERLINE = Format.UNDERLINE
RESET_FORMAT = Format.RESET
if not any(arg in argv for arg in ["--quiet", "-q"]):
# Check if a sound binary is present, default to aplay
if which("aplay"):
sound_binary = "aplay"
elif which("play"):
sound_binary = "play"
else:
print(
"Error: Unable to play sound effects. No supported sound binary found (aplay or play)."
)
if not any(arg in argv for arg in ["--disable-notifications", "-d"]):
display_toast = True
else:
display_toast = False
def play_sfx(sound_effect):
if operating_system == "Linux":
run(["aplay", "-q", sound_effect])
def toast(msg):
if operating_system != "Linux":
print(
"Notifications are not yet implemented for your operating system - sorry!"
)
elif operating_system == "Linux":
if which("notify-send"):
run(["notify-send", "Pomy", msg])
else:
print(
"Unable to display notification toasts. notify-send is not installed on this device."
)
def set_cycle_type():
global global_cycle_count
# Long break every 8th cycle
if global_cycle_count % 8 == 0:
is_work = False
return (
long_break_duration,
"You finished a set! Enjoy your well deserved rest!",
is_work,
)
# Short breaks are always even cycles
elif global_cycle_count % 2 == 0:
is_work = False
return (
short_break_duration,
"Drink water, breathe deeply and stretch!",
is_work,
)
# Pomodoro cycles are always odd
elif global_cycle_count % 2 != 0:
is_work = True
return (pomodoro_duration, "Work time - Let's do this!", is_work)
else:
is_work = True
return (pomodoro_duration, "Work time - Let's do this!", is_work)
# Functions
def countdown(duration):
while duration:
mins, secs = divmod(duration, 60)
timer = "Timer: {:02d}:{:02d}".format(mins, secs)
print(timer, end="\r")
sleep(1)
duration -= 1
def show_progress(count, progress_message=None, is_set=False, is_work=False):
"""
This function handles showing all progress messages, such as the current set,
or the timestamp and message of a new cycle.
Depending on the cycle type, it will construct two messages that will display the
exact same information, each one in a format best suited for terminal or toast notifications.
If the user has not disabled formatted or colored output, the {CONSTANTS} in terminal messages
will evaluate to ANSI escape codes that will format the text to make it easier to read.
Terminal notifications are always displayed. They look like this:
[HH:MM - Pomodoro# or Break Type] Cycle message (cycle duration)
Desktop notifications are displayed by default, but can be disabled using --disable-notifications or -d.
On Linux, they depend on notify-send. They look like this:
Pomy
HH:MM - Pomodoro# or Break Type
Duration: (cycle_duration)
Cycle message
"""
current_time = str(datetime.now().time())[:5]
if is_set:
toast_msg = f"Set {count} at {current_time}"
terminal_msg = (
f"{YELLOW}{BOLD}{UNDERLINE}Set {count} at {current_time}{RESET_FORMAT}"
)
# Print enough whitespace to delete the Timer: MM:SS line in the terminal
print(" " * MAX_TIMER_CHARACTERS)
elif is_work:
minutes = "25 minutes"
toast_msg = f"{current_time} - Pomodoro {count}\nDuration: {minutes}\n{progress_message}"
terminal_msg = f"{ORANGE}{BOLD}[{current_time} - Pomodoro {count}]{RESET_FORMAT} {progress_message} ({minutes})"
# Break messages
else:
global global_cycle_count
# Long breaks occur every 8th cycle
if global_cycle_count % 8 == 0:
minutes = "15 minutes"
toast_msg = (
f"{current_time} - Long break\nDuration: {minutes}\n{progress_message}"
)
terminal_msg = f"{SKY_BLUE}{BOLD}[{current_time} - Long break]{RESET_FORMAT} {progress_message} ({minutes})"
# Short breaks are always even cycles (cycle count % 2 = 0)
else:
minutes = "5 minutes"
toast_msg = (
f"{current_time} - Mini break\nDuration: {minutes}\n{progress_message}"
)
terminal_msg = f"{SKY_BLUE}{BOLD}[{current_time} - Mini break]{RESET_FORMAT} {progress_message} ({minutes})"
if display_toast:
toast(toast_msg)
print(terminal_msg)
while True:
try:
if pomodoro_count == 0:
show_progress(set_count, is_set=True)
next_cycle = set_cycle_type()
cycle_duration = next_cycle[0]
cycle_msg = next_cycle[1]
cycle_is_work = next_cycle[2]
if cycle_is_work:
pomodoro_count += 1
# Display a timestamped progress message
show_progress(pomodoro_count, progress_message=cycle_msg, is_work=cycle_is_work)
# Play sound effect in a separate thread so the program does not hang
if sound_binary:
if cycle_is_work:
sound_effect = pomodoro_sfx
else:
sound_effect = break_sfx
sfx_thread = threading.Thread(target=play_sfx, args=(sound_effect,))
sfx_thread.start()
countdown(cycle_duration)
global_cycle_count += 1
# Congratulate user after completing a set of 8 cycles
if global_cycle_count % 9 == 0:
global_cycle_count = 1
pomodoro_count = 0
set_count += 1
except KeyboardInterrupt:
print("\n\n[Session ended] Good job!")
exit()