-
Notifications
You must be signed in to change notification settings - Fork 0
/
pomodoro.py
218 lines (179 loc) · 6.86 KB
/
pomodoro.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
"""
A timer + RNG for skinnerian reinforcement.
User input:
1. a way to start the timer
2. a way to specify how long each task is
3. a way to reset session history
Output:
1. a countdown timer
2. an alert if you get the reward
3. a history of the session
Usage:
pomodoro.py [options]
Options:
-h, --help Show this help message and exit
"""
import os
import time
import random
import argparse
import tkinter as tk
from tkinter import messagebox
from datetime import date
def main():
"""
Output: gui
"""
handle_args()
config_contents = {
'length_of_tasks_in_minutes': 0,
'num_of_tasks_done': 0,
'daily_decay': False
}
stats_contents = {
'datetime': "",
'num_of_tasks_done': 0
}
config = "config.txt"
stats = "statistics.txt"
with open(config, "r") as handle:
lines = []
for line in handle:
lines.append(line.split(",")[1].strip())
print("Time between each session:", lines[0], "\n# of sessions done:",
lines[1], "\n Daily Decay Mode:", lines[2])
config_contents['length_of_tasks_in_minutes'] = float(lines[0])
config_contents['num_of_tasks_done'] = int(lines[1])
config_contents['daily_decay'] = lines[2].upper() == 'TRUE'
task_length_in_seconds = int(60*config_contents['length_of_tasks_in_minutes'])
with open(stats, "r") as handle:
lines = []
raw_lines = handle.readlines()[-2:]
for line in raw_lines:
lines.append(line.split(",")[1].strip())
stats_contents['datetime'] = str(lines[0])
print(stats_contents['datetime'])
if is_today(stats_contents['datetime']):
stats_contents['num_of_tasks_done'] = int(lines[1])
else:
stats_contents['num_of_tasks_done'] = 0
print(stats_contents['num_of_tasks_done'])
while True:
run_timer(task_length_in_seconds)
config_contents['num_of_tasks_done'] += 1
stats_contents['num_of_tasks_done'] += 1
update_config(config_contents, config)
update_statistics(stats_contents, stats, config_contents[
'num_of_tasks_done'])
# set new date to today's to avoid duplication of log entries
stats_contents['datetime'] = date.today().strftime("%m/%d/%y")
reward_prob = get_reward_probability(config_contents, stats_contents)
is_reward = check_reward(reward_prob)
if is_reward:
reward_alert()
def handle_args() -> None:
# handle script arguments
parser = argparse.ArgumentParser(description='A timer + rng for skinnerian\
reinforcement')
parser.parse_args()
def run_timer(seconds: int) -> None:
"""
decrement seconds and pass to print_timer()
"""
while seconds > 0:
print_timer(seconds)
time.sleep(1)
seconds -= 1
def print_timer(seconds_left: int) -> None:
"""
prints to the console how much time is left
"""
minutes, seconds = divmod(seconds_left, 60)
min_sec_format = '{:02d}:{:02d}'.format(minutes, seconds)
print(min_sec_format, end='\r')
def check_reward(reward_prob: float) -> bool:
"""
returns true if rng < reward_prob
"""
rng = random.random()
if rng < reward_prob:
return True
else:
return False
def update_config(contents: dict, config: str) -> None:
"""Writes to the config file the current number of tasks done"""
with open(config, "w") as handle:
for elem in contents.keys():
line = elem.upper() + ", " + str(contents[elem]).upper() + '\n'
handle.write(line)
def update_statistics(contents: dict, statistics: str, cur_tasks: int) -> None:
"""
Writes to the statistics file the current number of tasks done and date
"""
keys = list(contents.keys())
tasks_line = keys[1].upper() + ", " + str(contents['num_of_tasks_done']
).upper() + '\n'
date_line = keys[0].upper() + ", " + date.today().strftime(
"%m/%d/%y") + '\n'
# if date logged is today's, then edit 'num_of_tasks_done' line only
if is_today(contents['datetime']):
truncate_last_line_in_file(statistics)
with open(statistics, 'a', encoding='utf8') as handle:
handle.write('\n' + tasks_line)
# new date, new entry in log file and first task of the day
else:
tasks_line = keys[1].upper() + ", 1" + '\n'
with open(statistics, "a") as handle:
handle.write(date_line)
handle.write(tasks_line)
def is_today(saved_datetime: str) -> bool:
"""Checks if today is the same date as the one logged"""
if saved_datetime == date.today().strftime("%m/%d/%y"):
return True
return False
def truncate_last_line_in_file(filename: str) -> None:
"""Finds and removes the last line in a file"""
with open(filename, "rb+") as handle:
handle.seek(0, os.SEEK_END)
pos = handle.tell() - 1
while pos > 0 and handle.read(1) != b"\n":
pos -= 1
handle.seek(pos, os.SEEK_SET)
if pos > 0:
handle.seek(pos - 1, os.SEEK_SET)
handle.truncate()
def get_reward_probability(config_contents: dict, stats_contents: dict) -> float:
"""
returns the probability of a reward given number of tasks done
n.b assuming 4hrs * 30d = 120hrs to habit formation, reward prob should
be close to zero (~5%) after 120 hours worth of tasks has passed (this is
irregardless of the decay function)
0.05 = slope*total_time_spent_in_hours + 1
(for daily case: total_time_spent_in_hours = 4)
then solve for slope
"""
decay_goal = 0.05
if config_contents['daily_decay'] is True:
total_decay_time_in_hours = 4
num_of_tasks_done = stats_contents['num_of_tasks_done']
else:
total_decay_time_in_hours = 120
num_of_tasks_done = config_contents['num_of_tasks_done']
decay_slope = (decay_goal - 1) / total_decay_time_in_hours
task_length_in_minutes = config_contents['length_of_tasks_in_minutes']
total_time_spent_in_hours = task_length_in_minutes*num_of_tasks_done / 60
reward_prob = decay_slope * total_time_spent_in_hours + 1
# print("This is the decay slope: ", decay_slope)
# print("This is the reward prob: ", reward_prob)
return reward_prob
def reward_alert() -> None:
"""
popup if reward
"""
root = tk.Tk()
root.attributes('-topmost', 1)
root.withdraw()
messagebox.showwarning(
'You got a Reward!',
'Close when you\'re ready to start the timer again')
main()