-
Notifications
You must be signed in to change notification settings - Fork 0
/
Advanced Todo.py
548 lines (511 loc) · 24.8 KB
/
Advanced Todo.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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
import datetime, time
from calendar import month_name #list of 13 strings [0] = "", [1] = "January"
import shelve # For persistance: (dailies(list of strings, no need for tuples yet), tomorrow's list(same), uncompleted tasks/leftover tasks)
from progress.bar import ChargingBar
data = shelve.open('Persistant Data') #"dailies","tomorrowList","uncompleted","deadline"
if "dailies" not in list(data.keys()):
data["dailies"] = []
dailies = data["dailies"]
if "tomorrowList" not in list(data.keys()):
data["tomorrowList"] = []
tomorrowList = data["tomorrowList"]
if "uncompleted" not in list(data.keys()):
data["uncompleted"] = []
uncompleted = data["uncompleted"]
if "deadline" not in list(data.keys()):
data["deadline"] = []
deadline = data["deadline"]
print(dailies,tomorrowList, uncompleted,deadline)
data.close()
def menu(dailies,tomorrowList, uncompleted,deadline):
menuList = ["Create new list.","Create list for tomorrow.","Load list made yesterday","Review current daily tasks","Add new daily tasks","See Uncompleted Tasks","Move Current Task to Long-Term Tasks","Create new tasks and add to Long-Term Task","Report completion of Long-Term Tasks","Exit"]
choice = " "
while choice:
print("""Task List Creator:\n\nWhat would you like to do?\n""")
print(f"You have {len(uncompleted)} regular tasks and {len(deadline)} Long-Term Tasks left.")
for option in menuList:
print(f"{menuList.index(option) + 1}. {option}")
choice = input("Enter the number of your choice")
isValid = False # user input must meet certain criteria to change this to True and escape the loop
while isValid == False:
if choice.isalpha() == False and choice.isdecimal() == True:
if int(choice) in range(1,len(menuList)+1):
isValid = True
continue
else:
print("Your number is out of range. Try again.")
else:
print("Not a positive number. Enter a number")
choice = input("Enter the number of your choice")
choice = menuList[int(choice)-1] #switch out index for string
#actual selection. Careful when adding new menu options as we are using changable list indexes, not dictionary keys.
if choice == menuList[0]:
#1. Create new list.
completedList, uncompleted, deadline = quickList(uncompleted)
print(f"You have {len(uncompleted)} tasks left")
syncUncompleted(uncompleted)
elif choice == menuList[1]:
#2. Create list for tomorrow.
tomorrowList = buildTomorrow(tomorrowList)
#saves data to the shelve
syncTomorrow(tomorrowList)
print("List for tomorrow saved to database.")
elif choice == menuList[2]:
#3. Load list made yesterday
completedList, uncompleted = doList(tomorrowList,uncompleted) #tomorrow list becomes a regular "taskList" from this point on.
syncUncompleted(uncompleted)
print(f"You have {len(uncompleted)} tasks left")
#Can we perhaps clean all this up? (12 lines is alot for a menu! let's try to get to less than 6!)
tasksRemaining = [x for x in tomorrowList if x not in completedList]
if len(tasksRemaining) == 0:
print("Congrats. You completed all items on the the list")
else:
print(f"You completed {len(completedList)} and have {len(tasksRemaining)}.")
answer = ""
while answer != "y" or answer.lower() != "n":
answer = input("Are you finished with this list? \nEnter [y] to delete this list.\nEnter [n] to return to the main menu.")
if answer.lower() == "y":
tomorrowList.clear()
syncTomorrow(tomorrowList)
print("List deleted")
time.sleep(0.8)
break
elif answer.lower() == "n":
break
elif choice == menuList[3]:
#4. Review current daily tasks
dailies = reviewDailies(dailies)
elif choice == menuList[4]:
#5. Add new daily tasks
dailies = addDailies(dailies)
elif choice == menuList[5]:
#6. See Uncompleted Tasks
for task in uncompleted:
print(f"{uncompleted.index(task) + 1}. {task[0]}")
print(f"You have {len(uncompleted)} tasks left")
time.sleep(2)
elif choice == menuList[6]:
#7. Move Current Task to Long-Term Tasks
deadline, uncompleted = moveToLongList(deadline, uncompleted)
elif choice == menuList[7]:
#8. Create new tasks and add to Long-Term Task
deadline = appendLongList(deadline)
elif choice == menuList[8]:
#9. Report completion of Long-Term Tasks
deadline = doLongList(deadline)
elif choice == menuList[9]:
#10. Exit
print("Exiting program")
break #exit has been pressed
def autoAdd(d,u,t):
#adds daily tasks and uncompleted tasks
t = autoDailies(t,d)
t = autoDeadline(t)
counter = 0
#for u, it's a bit different as tasks in u have a datetime stamp already
print(f"You have {len(uncompleted)} uncompleted tasks left from last time")
for task in u:
if task[0] in [task2[0] for task2 in t]:
t.append((task[0] + str(len([task[0] in t]) + 1),task[1]))
print(f"{task[0]} is added to your new list.\n->It was originally added at {task[1]}.")
counter += 1
time.sleep(0.1)
else:
t.append((task))
print(f"{task[0]} is added to your new list.\n->It was originally added at {task[1]}.")
counter += 1
time.sleep(0.1)
print(f"{counter} previously uncompleted tasks added out of {len(u)}")
return t
def autoDailies(t,d):
counter = 0
yesOrNo = input(f"Do you wish to add your {len(d)} dailies?")
while yesOrNo.lower() not in ['yes','y','no','n']:
yesOrNo = input(f"Do you wish to add your {len(d)} dailies?")
if yesOrNo.lower() in ["yes","y"]:
for task in d:
t.append((task,microSecSlicer(datetime.datetime.now())))
print(f"{task} is added to your tasks at {microSecSlicer(datetime.datetime.now())}.")
counter += 1
time.sleep(0.1)
print(f"{counter} Dailies added out of {len(d)}")
return t
def autoDeadline(t): #tasklist
now = microSecSlicer(datetime.datetime.now())
startingLength = len(t)
global deadline
d = deadline.copy()
for task in deadline:
if task[1] <= now:
print(f'Your task {task[0]} is due.')
print(f'This task was due {now-task[1]} ago. So let\'t do this one first. Moving to top of your list.')
time.sleep(1)
t.insert(0,task)
d.remove(task)
print('Added to the top of your list.')
time.sleep(0.3)
if len(t)-startingLength == len(deadline)-len(d): # ie the task added to t and removed from d should be the same
deadline = d # change should affect global variable
syncDeadline(deadline) # sync to database
print('Deadlines removed from database. Remained deadlines: ', len(deadline))
else:
print(f"length of t: {len(t)}\nstarting length: {startingLength}\nlength of deadlines{len(deadline)}\nlength of d{len(d)}")
raise ArithmeticError
return t
def buildList(uncompleted):
print("Building List")
time.sleep(0.3)
taskList = []
taskList = autoAdd(dailies,uncompleted,taskList)
print("Add tasks. Enter \"stop\" when you are done.")
time.sleep(0.3)
testTuple = ("",None)
while testTuple[0].lower() != "stop":
testTuple = (input("what task do you want to add?"),microSecSlicer(datetime.datetime.now()))
if testTuple[0].lower() == "stop":
continue
else:
taskList.append(testTuple)
print(f"{testTuple[0]} is added to your tasks at {testTuple[1]}.")
return taskList
def buildTomorrow(tomorrowList):
print("Building List for Tomorrow")
time.sleep(0.3)
while True: # infinite loop. Need to use 'break' keyword to get out of this.
if len(tomorrowList) > 0:
print("You already have a list for tomorrow")
print(f"Here are your {len(tomorrowList)} tasks.")
for task in tomorrowList:
print(f"{tomorrowList.index(task) + 1}. {task[0]}")
answer = ""
while answer.lower() not in ["y", "n"]:
answer = input("Do you want to delete and start over? \nEnter [y] to delete this list and build a new one.\nEnter [n] to return to the main menu.")
if answer.lower() == "y":
tomorrowList.clear()
print("List deleted")
buildTomorrow(tomorrowList)
elif answer.lower() == 'n':
break
else:
tomorrowList = autoAdd(dailies, uncompleted, tomorrowList)
print("Add tasks. Enter \"stop\" when you are done.")
time.sleep(0.3)
testTuple = ("",None)
while testTuple[0].lower() != "stop":
testTuple = (input("what task do you want to add?"),microSecSlicer(datetime.datetime.now()))
if testTuple[0].lower() == "stop":
continue
else:
tomorrowList.append(testTuple)
print(f"{testTuple[0]} is added to your tasks at {testTuple[1]}.")
if len(tomorrowList) > 0:
return tomorrowList
else:
print("No items added to your list for tomorrow")
def doList(taskList, uncompleted):
###NOTE - ADAPT this code to return UNCOMPLETED tasks - DONE
global deadline
if len(taskList) == 0:
taskList.append(('Take it easy and finish a to-do list with no tasks!',microSecSlicer(datetime.datetime.now())))
print("Do tasks on list")
print(f"Here are your tasks. Enter the number of the task when you complete it:")
time.sleep(1)
print(f"Other keywords\n'stop' to end todays program and save remaining tasks.\n'sublist' to generate a sublist for a complex task.\n'focus' to set a timer and get one task done.")
completedTasks = []
with ChargingBar(max=len(taskList)) as bar: #the actual loop where user completes tasks
while len(taskList) != len(completedTasks):
counter = 0 #provides reference to the phantom list
print("Remaining tasks:")
for task in taskList:
if task not in completedTasks:
counter += 1
print(f"{counter}. {task[0]}")
update = input("\nEnter number of completed task: \n")
if update.lower() == "stop":
add2Uncompleted = [task for task in taskList if task not in completedTasks]
if len(add2Uncompleted) == len(taskList) - len(completedTasks):
uncompleted = add2Uncompleted
syncUncompleted(uncompleted)
print("The following uncompleted tasks will been added to your database. They will be loaded next time you create a list.")
counter = 0 # this allows for a shrinking list of remaining tasks, which is better for motivation.
for task in uncompleted:
counter += 1
print(f"{counter}. {task[0]}")
return completedTasks, uncompleted, deadline
else:
input("Something went wrong. Save your uncompleted tasks into a text file manually.\nPress enter when you are done.")
raise ArithmeticError
elif update.lower() == 'focus':
light = 'green'
while light == 'green':
update2 = input('Select the task to focus on with timer\n')
if update2.isdecimal() == True:
if int(update2)-1 in range(len([task for task in taskList if task not in completedTasks])):
light = 'red'
timer([task for task in taskList if task not in completedTasks][int(update2) - 1])
continue #user will still have to knock off the main task by themselves when they are done with the timer
elif update.lower() == 'sublist':
light = 'green'
while light == 'green':
update2 = input('Select the task to focus on with a sublist\n')
if update2.isdecimal() == True:
if int(update2)-1 in range(len([task for task in taskList if task not in completedTasks])):
light = 'red'
subList([task for task in taskList if task not in completedTasks][int(update2) -1])
continue
elif update.isdecimal() == True:
#essentially we are accessing the phantom list: "tasks in taskList but not in completedTasks"
update = int(update) - 1 # recieves reference to the phantom list
if update in range(len([task for task in taskList if task not in completedTasks])):
taskToUpdate = [task for task in taskList if task not in completedTasks][update] # needs to be assigned before we alter the phantomlist in next line
completedTasks.append(taskToUpdate) #NOTE, THIS MODIFIES THE PHANTOM LIST
updateLog(taskToUpdate) #updates log once task has been logged to the system
checkAllDailies(dailies,completedTasks)
bar.next()
print('\n')
else:
print("Your choice is out of range")
continue
else: # if input is not correct
print("Invalid input")
continue
if len(completedTasks) == len(taskList):
print("All tasks completed")
uncompleted.clear()
bar.finish()
syncUncompleted(uncompleted)
print(f"You have {len(uncompleted)} tasks left")
elif len(completedTasks) > len(taskList):
print("Something went wrong, length of length of completed Tasks is higher than the actual task list!")
print(f"You have {len(uncompleted)} tasks left")
elif len(completedTasks) < len(taskList):
print("not all tasks completed")
print(f"You have {len(uncompleted)} tasks left")
return completedTasks, uncompleted, deadline
def timer(task): #input
print(f"Ok! Let's focus on your task: '{task[0]}'!")
print("First, let's set a timer.")
minutes = int(input('How many minutes? >'))
seconds = int(input('How many seconds? >'))
print(f"You have {minutes} minutes and {seconds} seconds to complete the task:'{task[0]}'!")
timeRemaining = minutes * 60 + seconds
while timeRemaining > -1:
minutes, seconds = divmod(timeRemaining,60)
print('Time remaining: {:2d}:{:2d}'.format(minutes,seconds),end="\r")
time.sleep(1)
timeRemaining -= 1
print('\nTimes up!')
def subList(task):
print(f"You have selected {task[0]} to work on. Let\'s break this task down into managable sub-tasks.")
time.sleep(1)
subTasks = []
entry = ""
while entry.lower() != "stop":
entry = (input("what task do you want to add?\n"))
if entry.lower() == "stop":
continue
else:
subTasks.append(entry)
print(f"{entry} is added to your sublist for {task[0]}.")
with ChargingBar(max=len(subTasks)) as bar:
while len(subTasks) > 0:
print("\n")
for subTask in subTasks:
print(f"{subTasks.index(subTask) + 1}.{subTask}")
update = input('Enter the number of sub-task completed.')
if update.isdecimal() == True:
if int(update)-1 in range(len(subTasks)):
subTasks.pop(int(update) - 1)
bar.next()
time.sleep(1)
bar.finish()
time.sleep(1)
print("All done! Now you can take this tricky task off your list.")
time.sleep(1)
def doLongList(deadline):
taskNum = ""
while len(deadline) > 0:
for task in deadline:
print(f"{deadline.index(task)+1}. {task[0]}\nDate Due: {task[1]}")
taskNum = input("Enter the number of the task you have completed. Enter /stop/(lower case) to return to main menu.")
if taskNum == 'stop':
return deadline
valid = False
while valid == False:
if taskNum.isdecimal() == True:
if int(taskNum) in range(1,len(deadline)+1):
taskNum = int(taskNum) - 1
valid = True #breaks loop if input is number and in range of the length of deadline
continue
taskNum = input("Enter the number of the task you have completed. Enter /stop/(lower case) to return to main menu.")
updateLog(deadline[taskNum])
deadline.pop(taskNum)
syncDeadline(deadline)
return deadline
#three syncing methods for making any change to these variables persistant and keeping the database closed when not necessary
def syncTomorrow(tomorrowList):
data = shelve.open('Persistant Data')
data['tomorrowList'] = tomorrowList
data.close()
print('List for tomorrow synced to database')
def syncDailies(dailies):
data = shelve.open('Persistant Data')
data['dailies'] = dailies
data.close()
print('Dailies synced to database')
def syncUncompleted(uncompleted):
data = shelve.open('Persistant Data')
data['uncompleted'] = uncompleted
data.close()
print('Uncompleted tasks synced to database')
def syncDeadline(deadline):
data = shelve.open('Persistant Data')
data['deadline'] = deadline
data.close()
print('Deadlines synced to database')
#functions relating to CRUD for dailies below
def reviewDailies(dailies): #read and delete individual elements
answer = ''
while answer != "stop":
print("Current dailies")
for task in dailies:
print(f"{dailies.index(task) + 1}. {task}")
if len(dailies) == 0:
print("Sorry, no dailies found")
answer = input("Enter \"stop\" to exit. Or enter the number of a daily to DELETE.")
if answer.lower() == "stop":
continue
elif int(answer) -1 in range(0,len(dailies)): #Bug fix, etch int(answer) -1 in range(ZERO,len(dailies)) NO INCREMENT into your skin
dailies.pop(int(answer) - 1)
syncDailies(dailies)
return dailies
def addDailies(dailies):
newTask = ""
while newTask.lower() != "stop":
newTask = input("What task do you want to add?(enter \"stop\" to return to main menu.)")
if newTask.lower() == "stop":
continue
else:
dailies.append(newTask)
print(f"{newTask} is added to your daily tasks.")
syncDailies(dailies)
print("Dailies synced to database")
return dailies
#adds tasks with a specific deadline
def moveToLongList(deadline, uncompleted): #moves items from uncompleted to longlist
answer = ''
while answer != "stop":
print("Current uncompleted tasks.")
for task in uncompleted:
print(f"{uncompleted.index(task) + 1}. {task[0]}: Date Added: {task[1]}")
if len(uncompleted) == 0:
print("Sorry, no tasks found")
answer = input("Enter \"stop\" to exit. Or enter the number of the task you wish to move to long list.")
if answer.lower() == "stop":
continue
elif int(answer) - 1 in range(len(uncompleted)):
taskNoDeadline = uncompleted[int(answer)-1][0]
user_deadline = getDeadline()
taskWithDeadline = (taskNoDeadline,user_deadline)
deadline.append(taskWithDeadline)
uncompleted.pop(int(answer) - 1)
print(f"{taskWithDeadline[0]} added to long term tasks. Your deadline is {taskWithDeadline[1]}\n")
syncDeadline(deadline)
syncUncompleted(uncompleted)
return deadline, uncompleted
def appendLongList(deadline):
print("Current Deadlines")
for task in deadline:
print(f"{deadline.index(task) + 1}. {task[0]}: Deadline: {task[1]}")
newTask = ""
while newTask.lower() != "stop":
newTask = input("What task do you want to add?(enter \"stop\" to return to main menu.)")
if newTask.lower() == "stop":
continue
else:
user_deadline = getDeadline()
taskWithDeadline = (newTask,user_deadline)
deadline.append(taskWithDeadline)
print(f"{taskWithDeadline[0]} added to long term tasks. Your deadline is {taskWithDeadline[1]}\n")
syncDeadline(deadline)
print("Deadlines synced to database")
return deadline
def getDeadline():
print("Choose your deadline")
now = microSecSlicer(datetime.datetime.now())
possDeadlines = {"year": datetime.timedelta(days=365), "month": datetime.timedelta(days=31), "week": datetime.timedelta(weeks=1), "day": datetime.timedelta(days=1)}
userInput = input("Choose how long you need to do the task.\n/year/\n/month/\n/week/\n/day/\n/other/")
if userInput.lower() not in list(possDeadlines.keys()) + ['other']:
print('Incorrect input')
date1 = getDeadline()
return date1
elif userInput.lower() == 'other':
light = 'green'
while light == 'green':
try:
print("Setting deadline...")
time.sleep(0.3)
year = int(input('Enter a year'))
month = int(input('Enter a month(no.1-12)'))
day = int(input('Enter a day'))
hour = int(input('Enter an hour(0-23)'))
except ValueError:
print("Incorrect input")
continue
else:
conditions = [False if month == 2 and day not in range(1,29) else True, month in range(1,13), day in range(1,32), day in range(1,31) if month in [4,6,9,11] else True]
if all(conditions):
date1 = datetime.datetime(year, month, day, hour)
if isinstance(date1, datetime.datetime):
light = 'red'
return date1
else:
print("Not in specified range!")
else:
date1 = now + possDeadlines[userInput]
return date1
def updateLog(task):
#4 parts open file corresponding to today - DONE write to file - DONE check if all dailies are completed - DONE(moved to separate function) close file DONE
#no need to return anything
description = input(f'Enter your description of the following task: \"{task[0]}\"\n')
now = microSecSlicer(datetime.datetime.now())
timeTaken = now - task[1]
title = f'Todo list - quicklist - {now.year}-{now.month}-{now.day}.txt'
todo = open(title, 'a')
todo.write(f"{task[0]} - TASK COMPLETE\nIt took you(d, h:m:s): {timeTaken}\n=>Task desciption: {description}\n")
todo.close()
print("Closed text log.")
def checkAllDailies(dailies,completed):
#if completed tasks(including this task are in dailies: print(affirmation that you did well)
#-> How do I get this logic? - convert completed to a list of strings, add current task, then create a list of completed dailies, if len = len(dailies) then print your message
#note: will compare length because order matters:
""" >>> ab = ['a','b']
>>> ba = ['b','a']
>>> ab == ba
False
"""
#note2: will use set() to remove potential duplicates
completedList = []
for t in completed:
completedList.append(t[0])
completedDailies = set([t for t in completedList if t in dailies])
if len(completedDailies) == len(dailies):
time.sleep(1)
print('Well Done, you completed all daily tasks!')
time.sleep(1)
now = microSecSlicer(datetime.datetime.now())
todo = open(f'Todo list - quicklist - {now.year}-{now.month}-{now.day}.txt', 'a')
todo.write(f'Well Done! You completed all {len(dailies)} daily tasks')
print(f'Well Done! You completed all {len(dailies)} daily tasks')
todo.close()
print("Closed text log.")
def quickList(uncompleted): # builds list from scratch and then completes it by combining two functions.
completedList, uncompleted, deadline = doList(buildList(uncompleted), uncompleted)
print(f"You have {len(uncompleted)} tasks left")
return completedList, uncompleted, deadline
def microSecSlicer(datetimeObject):
datetimeString = str(datetimeObject)
datetimeString = datetimeString[0:datetimeString.find(".")]
datetimeObject = datetime.datetime.strptime(datetimeString, "%Y-%m-%d %H:%M:%S")
return datetimeObject
menu(dailies,tomorrowList,uncompleted,deadline)