-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstudy_time_tally.py
2059 lines (1984 loc) · 128 KB
/
study_time_tally.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
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import json
import sys
import time
from datetime import timedelta, datetime
from math import ceil
from os import path
from tools.file import save_json
import sqlite3
from contextlib import closing
from tools import log_tools
"""Study Time Tally
By Michael Fulcher
Send Donations (recommended $1.50USD) to -
PayPal: mjfulcher58@gmail.com
Bitcoin: 3DXiJxie6En3wcyxjmKZeY2zFLEmK8y42U
Other options @ http://michaelfulcher.yolasite.com/
"""
'''[0] Name of Subject, [1] Days to iterate required hours up and Amount of hours per day in,
[2] Start date, [3] End Date, [4] Hours done'''
def menu_char_check(a_word: str, options: str) -> bool:
message = " is not a valid input"
if len(a_word) == 0:
print("Enter" + message)
elif len(a_word) == 1 and a_word in options:
return True
else:
print(a_word + message)
return False
'''Found @: https://stackoverflow.com/questions/16870663/how-do-i-validate-a-date-string-format-in-python#_=_'''
def validate_date(date_text: str) -> bool:
try:
datetime.strptime(date_text, '%d/%m/%Y').date()
return True
except ValueError:
print(date_text + " not a valid input")
return False
def get_date() -> str:
while 1:
date = input("Enter Date (dd/mm/yyyy):")
if validate_date(date):
return date
def validate_start_date(end_date: str, old: str) -> str:
while 1:
print(f"\nStart Date, format - dd/mm/yyyy. Current Date ({old})")
start_date = get_date()
if datetime.strptime(end_date, '%d/%m/%Y').date() < datetime.strptime(start_date, '%d/%m/%Y').date():
print(f"Start date must be before the end date ({end_date}).")
else:
return start_date
def validate_end_date(start_date: str, old: str = None) -> str:
while 1:
if old:
print(f"\nEnd Date, format - dd/mm/yyyy. Current Date ({old})")
else:
print("\nEnd Date, format - dd/mm/yyyy.")
end_date = get_date()
if datetime.strptime(end_date, '%d/%m/%Y').date() < datetime.strptime(start_date, '%d/%m/%Y').date():
print(f"End date must be after the start date ({start_date}).")
else:
return end_date
'''found @: https://stackoverflow.com/questions/9044084/efficient-date-range-overlap-calculation-in-python
requires passed vars to be datetime objects'''
def overlap_check(start_of_range1, end_of_range1, start_of_range2, end_of_range2) -> bool:
latest_start, earliest_end = max(start_of_range1, start_of_range2), min(end_of_range1, end_of_range2)
delta = (earliest_end - latest_start).days + 1
return True if delta > 0 else False
def tally_hours(start_date: str, end_date: str, day_dict: dict, cur=None) -> int:
date_start, date_today = datetime.strptime(start_date, '%d/%m/%Y').date(), datetime.today().date()
if date_today < date_start:
return 0
date_end = datetime.strptime(end_date, '%d/%m/%Y').date()
if date_end < date_today:
date_today = date_end
range_list = [[date_start, date_today]]
## if check_holiday
holiday_list = list()
if cur:
for holiday in cur.execute("SELECT startDate, endDate from Holiday WHERE (JULIANDAY(MIN(endDate, (?))) - JULIANDAY(MAX(startDate, (?)))) + 1 > 0", (date_today.strftime('%Y-%m-%d'), date_start.strftime('%Y-%m-%d'))).fetchall():
holiday_list.append([datetime.strptime(holiday[0], '%Y-%m-%d').date(), datetime.strptime(holiday[1], '%Y-%m-%d').date()])
else:
if 'holidays' in data and len(data['holidays']) > 0:
for name, date in data['holidays'].items():
holiday_start, holiday_end = datetime.strptime(date[0], '%d/%m/%Y').date(), datetime.strptime(date[1], '%d/%m/%Y').date()
if overlap_check(date_start, date_today, holiday_start, holiday_end):
holiday_list.append([holiday_start, holiday_end])
one_day = timedelta(days=1)
rangelist_size = 1
for holiday in holiday_list:
n = 0
while n < rangelist_size: # holiday_start = holiday[0], holiday_end = holiday[1]
# term_end = range_list[n][1], term_start = range_list[n][0]
if holiday[0] > range_list[n][1] or holiday[1] < range_list[n][0]: # filter out pointless tests
n += 1
continue
if holiday[0] <= range_list[n][0]: # holiday starts before or start of term
if holiday[1] >= range_list[n][1]:
range_list.pop(n)
rangelist_size -= 1
else:
range_list[n][0] = holiday[1] + one_day
n += 1
else: # holiday starts after 'term' starts.
new_end = holiday[0] - one_day
if holiday[1] < range_list[n][1]:
range_list.append([holiday[1] + one_day, range_list[n][1]])
range_list[n][1] = new_end
rangelist_size += 1
break
else:
range_list[n][1] = new_end
n += 1
weeks_hours = n = 0 # n var repurposed to grand total of hours
for hour in day_dict.values():
weeks_hours += hour
for i in range_list:
weeks = i[0].weekday()
days = (i[1] - i[0]).days + (1 if weeks == 0 else weeks - 6)
while weeks not in [0, 7]:
sch = str(weeks)
if sch in day_dict.keys():
n += day_dict[sch]
weeks += 1
weeks = days // 7 # weeks repurposed to 'number of weeks'
days = days % 7 # days var repurposed to length of week.
n += weeks * weeks_hours
if days != 0:
weeks = i[1].weekday() # weeks repurposed to 'day of week number'
while days >= 0:
sch = str(weeks)
if sch in day_dict.keys():
n += day_dict[sch]
days -= 1
if weeks == 0:
weeks = 6
else:
weeks -= 1
return n
def tabs_list(a_list: list): # generate data for correct spacing in data display columns
t_width = n = 0
tabs = list()
for item_name in a_list:
tabs.append(len(item_name))
if tabs[n] > t_width:
t_width = tabs[n]
n += 1
n -= 1
add_space = True if t_width % 8 == 0 else False
t_width = ceil(t_width / 8)
while n >= 0:
tabs[n] = t_width - tabs[n] // 8
n -= 1
return t_width, add_space, tabs
def display_holiday_list_db(add_numbers: bool, names_list: dict, id_range: dict, for_removal: list, changed_names: dict, changed_start: dict, changed_end: dict) -> int:
new_name_list = list()
names_list_keys = [*names_list.keys()]
for id, name in names_list.items():
if id in changed_names.keys():
new_name_list.append(changed_names[id])
else:
new_name_list.append(name)
new_keys = list()
for id, name in changed_names.items():
if id not in names_list_keys:
new_name_list.append(name)
new_keys.append(id)
nums = names_list_keys + new_keys
t_width, add_space, tabs = tabs_list(new_name_list)
add_numbers_str, tab_section = '\t' if add_numbers else '', '\t' * t_width
print(f"\nSaved Holidays:\n{add_numbers_str}Name{tab_section}{' ' if add_space else ''}Start Date\tEnd Date")
n = 0
for id in nums:
add_numbers_str, tab_section = str(n) + '.\t' if add_numbers else '', '\t' * tabs[n]
print(f"{add_numbers_str}{changed_names[id] if id in changed_names.keys() else names_list[id]}{tab_section}{' ' if add_space else ''}{changed_start[id] if id in changed_start.keys() else id_range[id][0]}\t{changed_end[id] if id in changed_end.keys() else id_range[id][1]}\t{'(Removed)' if id in for_removal else ''}")
n += 1
return n
def display_holiday_list(add_numbers: bool = False) -> int:
t_width, add_space, tabs = tabs_list([*data['holidays'].keys()])
add_numbers_str, tab_section = '\t' if add_numbers else '', '\t' * t_width
print(f"\nSaved Holidays:\n{add_numbers_str}Name{tab_section}{' ' if add_space else ''}Start Date\tEnd Date")
n = 0
for holiday, dates in data['holidays'].items():
add_numbers_str, tab_section = str(n) + '.\t' if add_numbers else '', '\t' * tabs[n]
print(f"{add_numbers_str}{holiday}{tab_section}{' ' if add_space else ''}{dates[0]}\t{dates[1]}")
n += 1
return n
def hours_display(item1: str, item2: str) -> str:
output = str()
if settings['display completed %']:
output += item1
else:
if settings['display extra completed'] and not settings['display extra completed %']:
output += '\t'
if settings['display extra completed']:
if settings['display completed %']:
output += '\t'
else:
if settings['display extra completed %']:
output += '\t'
output += item2
return output
def display_menu_stats(name: str, startDate, endData, days: dict, normal_hours_done: int, normal_minutes_done: int, extra_hours_done: int, extra_minutes_done: int, num_tabs: int, add_space: bool, curs=None) -> None:
num_hours = tally_hours(startDate, endData, days, curs)
tabs_section = "\t" * num_tabs
line = f'{name}{tabs_section}{" " if add_space else ""}{num_hours}\t\t{normal_hours_done}h {normal_minutes_done}m\t'
if normal_hours_done == 0 and normal_minutes_done == 0 or num_hours == 0:
ratio = 0
zero_in_normal_hours = True
else:
ratio = (normal_hours_done * 60 + normal_minutes_done) / (num_hours * 60)
zero_in_normal_hours = False
line += hours_display(f'\t{ratio * 100:.1f}%', f'|{extra_hours_done}h {extra_minutes_done}m\t')
if settings['display extra completed %']:
line += '\t ' if settings['display extra completed'] else '\t|'
ratio = 0 if zero_in_normal_hours and extra_hours_done == 0 and extra_minutes_done == 0 else ((normal_hours_done + extra_hours_done) * 60 + normal_minutes_done + extra_minutes_done) / (num_hours * 60)
line += f'{ratio * 100:.1f}%'
print(line)
def get_enabled_subjects(db_loc: str) -> list:
with closing(sqlite3.connect(db_loc)) as db_con:
with closing(db_con.cursor()) as cur:
return cur.execute("SELECT ID, subjectName FROM Subject WHERE enabled = 1").fetchall()
def main_menu() -> chr:
global last_past_hours_update
valid_option, past_hours_reset = False, True
if settings['useDB']:
print('\tDatabase Mode')
subjects = get_enabled_subjects(database_path)
if subjects:
items_present = True
t_width, add_space, tabs = tabs_list([i[1] for i in subjects])
else:
items_present = False
# add work hours needed to meet obligation
else:
print('\tJSON Mode')
if "subjects" in data and len(data["subjects"]) > 0:
items_present = True
t_width, add_space, tabs = tabs_list([i[0] for i in data['subjects']])
else:
items_present = False
while not valid_option:
if items_present:
if settings['useRecentHoursDisplay']:
if check_fix_daily_progress() or past_hours_reset:
past_hours_reset = False
dailykey_to_dayname = dict()
n = last_past_hours_update.weekday()
for x in daily_progress_keys[::-1]:
if x in past_hours:
dailykey_to_dayname[x] = n
n -= 1
if n == -1:
n = 6
print(f"\n\t{'Recent Activity:':^{20}}")
for x in daily_progress_keys:
if x in past_hours and (past_hours[x][1] != 0 or past_hours[x][0] != 0):
print(f"\t{weekdays[dailykey_to_dayname[x]]:^{9}}: {past_hours[x][0]}h {past_hours[x][1]}m")
tabs_section = '\t' * t_width
line_end = hours_display("\t%", "|Extra Completed")
line = f'\nTally totals:\nName{tabs_section}{" " if add_space else ""}Hours Owing\tHours Completed{line_end}'
if settings['display extra completed %']:
if not settings['display extra completed']:
line += '\t|'
line += ' %'
print(line)
n = 0
if settings['useDB']:
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
for subject_item in subjects:
date_range = cur.execute(f"SELECT startDate, endDate FROM Subject WHERE ID = (?)", (subject_item[0],)).fetchone()
days = cur.execute(f"SELECT dayCode, hours FROM SubjectDay WHERE subjectID = (?)", (subject_item[0],)).fetchall()
days_dict = dict()
for item in days:
days_dict[str(item[0])] = item[1]
normal_time_result = [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 0", (subject_item[0],)).fetchone()]
extra_time_result = [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType IN (1, 2)", (subject_item[0],)).fetchone()]
correct_time_format_display(normal_time_result)
correct_time_format_display(extra_time_result)
display_menu_stats(subject_item[1], datetime.strptime(date_range[0], '%Y-%m-%d').strftime('%d/%m/%Y'), datetime.strptime(date_range[1], '%Y-%m-%d').strftime('%d/%m/%Y'), days_dict, normal_time_result[0], normal_time_result[1], extra_time_result[0], extra_time_result[1], tabs[n], add_space, cur)
n += 1
else:
for subj in data["subjects"]:
display_menu_stats(subj[0], subj[2], subj[3], subj[1], subj[4][0], subj[4][1], subj[5][0], subj[5][1], tabs[n], add_space)
n += 1
else:
print("Subject list is empty.")
print('\n\t--MAIN MENU--\n\tAdd to (T)ally\n\tUse t(I)mer to Add to Tally\n\t(D)isplay Timesheet\n\t(A)dd Subject\n\t(R)emove Subject\n\t(E)dit Subject\n\t(H)olidays Menu\n\t(S)ettings Menu\n\te(X)it')
menu_option = input("Choose Option:").upper()
valid_option = menu_char_check(menu_option, 'TIXDAREHS')
return menu_option
"""
Check if day has changed since last daily progress dictionary was updated.
If the day has changed then data and keys are re-arranged to the correct weekdays.
"""
def check_fix_daily_progress():
global last_past_hours_update
n = (datetime.now().date() - last_past_hours_update).days
if n == 0:
return False
for j in range(n):
for i in range(7):
if i == 6:
past_hours[daily_progress_keys[6]] = [0, 0]
else:
if daily_progress_keys[i + 1] not in past_hours:
if daily_progress_keys[i] in past_hours:
past_hours.pop(daily_progress_keys[i])
else:
past_hours[daily_progress_keys[i]] = past_hours[daily_progress_keys[i + 1]]
last_past_hours_update = datetime.now().date()
return True
def print_selected_days(day_list: list) -> None:
if day_list:
print("\nSelected Days:")
for i in day_list:
print(weekdays[int(i)])
else:
print("No days Selected")
def setup_day_options(start_date, end_date, need_length: bool = True):
"""Uses global day_options = "MTWTFSS" to generate options according to length of time period (end_date_obj - start_date_obj).days + 1"""
if type(start_date) == str:
start_date_obj = datetime.strptime(start_date, '%Y-%m-%d' if settings['useDB'] else '%d/%m/%Y').date()
end_date_obj = datetime.strptime(end_date, '%Y-%m-%d' if settings['useDB'] else '%d/%m/%Y').date()
else:
start_date_obj = start_date
end_date_obj = end_date
length = (end_date_obj - start_date_obj).days + 1
if length == 1:
if need_length:
return day_options[start_date_obj.weekday()], length
else:
return day_options[start_date_obj.weekday()]
elif length < 7:
first_day = start_date_obj.weekday()
last_day = first_day + length
s_day_options = day_options[first_day:last_day]
if last_day > 7:
last_day -= 7
s_day_options = day_options[0:last_day] + s_day_options
if need_length:
return s_day_options, length
else:
return s_day_options
else:
if need_length:
return day_options, length
else:
return day_options
def check_digit(num_str: str, allow_enter_be_zero: bool):
if num_str == '':
if allow_enter_be_zero:
return True, 0
else:
print('<Enter> is not valid input.')
return False, 0
elif num_str.isdecimal():
return True, int(num_str)
else:
print("Input must be a decimal number.")
return False, 0
def get_time_digit(time_div_str: str) -> int:
valid_num = False
while not valid_num:
selector = input(f'Number of {time_div_str}(s) to add:')
valid_num, digit = check_digit(selector, True)
return digit
def get_hour(day_num: int) -> int:
valid_num = False
while not valid_num:
selector = input(f"Number of hours for {weekdays[day_num]}:")
valid_num, digit = check_digit(selector, False)
return digit
def create_days_dict(s_day_options: str, length: int, return_day_list: bool = False, days=None):
"""Creates dictionary of given options str, length, return_day_list: bool, days dict: optional
Returns dict of days
if return_day_list = True
Returns dict of days, days_options str"""
if days is None:
days = dict()
if length == 1:
length = day_options.find(s_day_options) # length is repurposed to index of 'day_option' index
day_str = str(length)
days[day_str] = None
print("Course is one day, day is auto-set to " + weekdays[length])
else:
day_pick_number = 0
if return_day_list:
day_list = list()
while 1:
print_selected_days(days)
output = str()
for d in s_day_options:
output += menu_weekdays[day_options.find(d)] + ", "
print(f'\n--SELECT DAYS--\n{output[:-2]}\nEnter X when finished.')
selector = input('Option:').upper()
if menu_char_check(selector, s_day_options + 'X'):
if selector == "X":
break
if settings['useDB']:
day_options_pos = day_options.find(selector)
days[day_options_pos] = None
day_str = str(day_options_pos)
else:
day_str = str(day_options.find(selector))
days[day_str] = None
if return_day_list:
day_list.append(day_str)
day_pick_number += 1
if day_pick_number == length:
break
n = s_day_options.rfind(selector)
s_day_options = s_day_options[:n] + s_day_options[n + 1:]
for d in days:
if not days[d]:
days[d] = get_hour(int(d))
if return_day_list:
return days, day_list
else:
return days
def get_name(type_text: str, name_list: list) -> str:
while 1:
name = input(f"Name of {type_text}:").strip()
if name:
if name in name_list:
print(f"Name {name} already exists.")
else:
return name
else:
print("Name must be at least 1 character.")
def add_menu_common():
print('\n\t--ADD SUBJECT--')
print("\n\tStart Date, format - dd/mm/yyyy.")
start = get_date()
end = validate_end_date(start)
days = create_days_dict(*setup_day_options(datetime.strptime(start, '%d/%m/%Y').date() if settings['useDB'] else start, datetime.strptime(end, '%d/%m/%Y').date() if settings['useDB'] else end))
return days, start, end
def add_menu_db() -> None:
while 1:
name = input(f"Name of Subject:").strip()
if name:
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
result = cur.execute("SELECT ID, enabled FROM Subject WHERE subjectName IS (?)", (name,)).fetchone()
if result:
if result[1] == 0: ## if already exists and is disabled, ask to re-enable
while (field := input(f'Name {name} already exists and is disabled, do you want to enable it, (Y)es or (N)o?').upper()) != 'N':
if field == 'Y':
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
db_trans_change_display_setting(cur, result[0], name, True)
db_con.commit()
return
else:
print(f"Name {name} already exists.")
else:
break
else:
print("Name must be at least 1 character.")
days, start_date, end_dat = add_menu_common()
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
cur.execute("INSERT INTO Subject (subjectName, startDate, endDate, enabled) VALUES (?, ?, ?, 1)", (name, datetime.strptime(start_date, '%d/%m/%Y').strftime('%Y-%m-%d'), datetime.strptime(end_dat, '%d/%m/%Y').strftime('%Y-%m-%d')))
result = cur.execute("SELECT ID FROM Subject WHERE subjectName IS (?)", (name,)).fetchone()[0]
for d, hour in days.items():
cur.execute("INSERT INTO SubjectDay (subjectID, dayCode, hours) VALUES (?, ?, ?)", (result, int(d), hour))
db_con.commit()
log_tools.tprint("DB Add: Subject - " + name)
def add_menu() -> list:
name = get_name('Subject', [x[0] for x in data['subjects']])
log_tools.tprint("Added Subject - " + name)
return [name, *add_menu_common(), [0, 0], [0, 0]]
def display_subject_list() -> int:
t = 0
for subj in data["subjects"]:
print("\t{} - {}".format(str(t), subj[0]))
t += 1
return t
def validate_selector(a_string: str, num: int, start_num: int = 0) -> bool:
if a_string.isnumeric():
a = int(a_string)
if start_num <= a < num:
return True
else:
print(a_string + " is not a valid option")
return False
def remove_menu() -> bool:
if settings['useDB']:
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
subjects = cur.execute("SELECT ID, subjectName, enabled FROM Subject").fetchall()
state_list = [subj[2] for subj in subjects]
length = len(subjects)
if length > 0:
k, j = len(str(length)), 0
for subj in subjects:
length = len(subj[1])
if j < length:
j = length
j += 2
l = j + k + 11
while 1:
n = 0
print(f"\n\t{'--Disable/Enable MENU--':^{l}}")
for subj in subjects:
n += 1
print(f"\t{n:>{k}} - {subj[1]:<{j}}{'Enabled' if state_list[n - 1] == 1 else 'Disabled'}")
selector = input("Subject to disable or enable:").upper()
if selector == 'X':
break
elif validate_selector(selector, length + 1, 1):
selector = int(selector) - 1
state_list[selector] = 0 if state_list[selector] == 1 else 1
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
cur.execute("UPDATE Subject SET enabled = (?) WHERE ID = (?)", (state_list[selector], subjects[selector][0]))
db_con.commit()
log_tools.tprint(f"DB Update: Subject {subjects[selector][1]} {'disabled' if state_list[selector] == 0 else 'enabled'}")
else:
print('No subjects found in DB')
else:
if "subjects" in data and len(data['subjects']) > 0:
while 1:
print("\n--REMOVE MENU--")
n = display_subject_list()
selector = input("Subject to remove:").upper()
if selector == "X":
break
elif validate_selector(selector, n):
old = data["subjects"].pop(int(selector))[0]
log_tools.tprint("Removed Subject - " + old)
save_json(data, data_path + 'study_tally_data.json', "Data")
break
else:
print("\nSubject list is empty.")
def check_day_range(start_date_obj, end_date_obj, old_day_range):
new_day_range = setup_day_options(start_date_obj, end_date_obj, False)
new_day_range_list = [str(day_options.find(x)) for x in new_day_range]
day_list = list() # days that may be removed later
day_range_change = False
for d in old_day_range:
if d not in new_day_range_list:
day_range_change = True
day_list.append(d)
return day_range_change, day_list
def remove_days_from_dict(subject: int, remove_list: list) -> None:
for i in remove_list:
data['subjects'][subject][1].pop(i)
def check_date_new_days_dict(length: int, new_start_date_obj, new_end_date_obj, old_days_dict: dict):
if length < 7:
day_range_change, day_list = check_day_range(new_start_date_obj, new_end_date_obj, [*old_days_dict.keys()])
log_output = str()
question_str = "Is this correct?\n(Y)es to make changes\n(N)o to cancel changes\n:"
for day in day_list:
log_output += weekdays[int(day)] + ", "
log_out_msg = log_output[:-2] + " will be removed.\n"
if length == 1:
while 1:
day_of_week = new_start_date_obj.weekday()
day_of_week_str = str(day_of_week)
selector = input(f"\nDetected that your new day range is one day, day will be auto-set to {weekdays[day_of_week]}{'.' if len(log_output) == 0 else ' and ' + log_out_msg}{question_str}").upper()
if selector == "N":
return old_days_dict
elif selector == "Y":
if day_of_week_str in old_days_dict.keys():
for i in day_list:
old_days_dict.pop(i)
else:
for i in day_list:
old_days_dict.pop(i)
old_days_dict[day_of_week_str] = get_hour(int(day_of_week))
log_tools.tprint(f"Removed {log_output[:-2]}. (Not Saved:DB Mode)")
return old_days_dict
else:
print("Invalid input.")
else:
while 1:
selector = input(f"\nDetected that your new day range is less than the old day range.\n{log_out_msg}{question_str}").upper()
if selector == "N":
return old_days_dict
elif selector == "Y":
for i in day_list:
old_days_dict.pop(i)
log_tools.tprint(f"Removed {log_output[:-2]}. (Not Saved:DB Mode)")
return old_days_dict
else:
print("Invalid input.")
else:
return old_days_dict
def change_date_json(subject: int, date2change: int, date_word_str: str, length: int, new_start_date_obj, new_end_date_obj, new_date: str) -> None:
if length < 7:
day_range_change, day_list = check_day_range(new_start_date_obj, new_end_date_obj, [*data['subjects'][subject][1].keys()])
if day_range_change:
log_output = str()
question_str = "Is this correct?\n(Y)es to make changes\n(N)o to cancel changes\n:"
for day in day_list:
log_output += weekdays[int(day)] + ", "
log_out_msg = log_output[:-2] + " will be removed.\n"
if length == 1:
while 1:
day_of_week = new_start_date_obj.weekday()
day_of_week_str = str(day_of_week)
selector = input("\nDetected that your new day range is one day, day will be auto-set to {}{s}{a}".format(weekdays[day_of_week], s="." if len(log_output) == 0 else " and " + log_out_msg, a=question_str)).upper()
if selector == "N":
break
elif selector == "Y":
old = data['subjects'][subject][date2change]
data['subjects'][subject][date2change] = new_date
if day_of_week_str in data['subjects'][subject][1].keys():
remove_days_from_dict(subject, day_list)
log_tools.tprint("Changed {} {} date from {} to {} and removed {}. (Not Saved)".format(data['subjects'][subject][0], date_word_str, old, data['subjects'][subject][date2change], log_output[:-2]))
else:
remove_days_from_dict(subject, day_list)
data['subjects'][subject][1][day_of_week_str] = get_hour(int(day_of_week))
log_tools.tprint("Changed {} {} date from {} to {}, added {} and removed {}. (Not Saved)".format(data['subjects'][subject][0], date_word_str, old, data['subjects'][subject][date2change], weekdays[day_of_week], log_output[:-2]))
break
else:
print("Invalid input.")
else:
while 1:
selector = input("\nDetected that your new day range is less than the old day range.\n{}{}".format(log_out_msg, question_str)).upper()
if selector == "N":
break
elif selector == "Y":
remove_days_from_dict(subject, day_list)
old = data['subjects'][subject][date2change]
data['subjects'][subject][date2change] = new_date
log_tools.tprint("Changed {} {} date from {} to {} and removed {}. (Not Saved)".format(data['subjects'][subject][0], date_word_str, old, data['subjects'][subject][date2change], log_output[:-2]))
break
else:
print("Invalid input.")
else:
old = data['subjects'][subject][date2change]
data['subjects'][subject][date2change] = new_date
log_tools.tprint("Changed {} {} date from {} to {}. (Not Saved)".format(data['subjects'][subject][0], date_word_str, old, data['subjects'][subject][date2change]))
def edit_time_tally(subject_choice: int, time_category: int) -> None:
selector = None
print("\n--Edit {a} Tally of {b}--\nCurrent Tally: {c}h {d}m\nChanges are done by subtracting, not by saving the literal input.".format(a="Normal" if time_category == 4 else "Extra", b=data['subjects'][subject_choice][0], c=str(data['subjects'][subject_choice][time_category][0]), d=str(data['subjects'][subject_choice][time_category][1])))
while selector != 'X':
selector = input("--Field to edit:\n1. Hour\n2. Minute\ne(X)it without saving\n:").upper()
if selector == '1':
while 1:
selector = input("Decrease hour by:").upper()
if selector == 'X':
break
elif selector.isdigit():
n = int(selector)
if data['subjects'][subject_choice][time_category][0] < n:
print("Amount to subtract cannot be larger than current hours ({}h)".format(str(data['subjects'][subject_choice][time_category][0])))
else:
old = data['subjects'][subject_choice][time_category][0]
data['subjects'][subject_choice][time_category][0] -= n
log_tools.tprint("Decreased {} - hour by {} from {}h to {}h. (Not Saved).".format(data['subjects'][subject_choice][0], selector, str(old), str(data['subjects'][subject_choice][time_category][0])))
selector = 'X'
break
else:
print("Input must be a number")
elif selector == '2':
while 1:
selector = input("Decrease minutes by (can subtract greater than 59minutes):").upper()
if selector == 'X':
break
elif selector.isdigit():
n, old = int(selector), data['subjects'][subject_choice][time_category][0] * 60 + data['subjects'][subject_choice][time_category][1]
if n > old:
print("Amount to subtract cannot be larger then current hours + minutes ({}h {}m)".format(str(data['subjects'][subject_choice][time_category][0]), str(data['subjects'][subject_choice][time_category][1])))
else:
old, old_minute = data['subjects'][subject_choice][time_category][0], data['subjects'][subject_choice][time_category][1]
data['subjects'][subject_choice][time_category][1] -= n
while data['subjects'][subject_choice][time_category][1] < 0:
data['subjects'][subject_choice][time_category][1] += 60
data['subjects'][subject_choice][time_category][0] -= 1
log_tools.tprint("Decreased {} - {a} time by {b} from {oh}h {om}m to {h}h {m}m. (Not Saved).".format(data['subjects'][subject_choice][0], a="Normal" if time_category == 4 else "Extra", b=selector, oh=str(old), om=str(old_minute), h=str(data['subjects'][subject_choice][time_category][0]), m=str(data['subjects'][subject_choice][time_category][1])))
selector = 'X'
break
else:
print("Input must be a number")
elif selector != "X":
print("Invalid input.")
def edit_time_tally_db(subject_name: str, time_category: str, old_hour: int, old_minute: int) -> tuple:
selector = None
while old_minute > 59:
old_hour += 1
old_minute -= 60
print(f"\n--Edit {time_category} Tally of {subject_name}--\nCurrent Tally: {old_hour}h {old_minute}m\nChanges are done by subtracting, not by saving the literal input.")
while selector != 'X':
selector = input("--Field to edit:\n1. Hour\n2. Minute\ne(X)it without saving\n:").upper()
if selector == '1':
while 1:
selector = input("Decrease hour by:").upper()
if selector == 'X':
return None
elif selector.isdigit():
n = int(selector)
if old_hour < n:
print(f"Amount to subtract cannot be larger than current hours ({old_hour}h)")
else:
return n, None
else:
print("Input must be a number")
elif selector == '2':
while 1:
selector = input("Decrease minutes by (can subtract greater than 59minutes):").upper()
if selector == 'X':
return None
elif selector.isdigit():
n = int(selector)
if n > old_hour * 60 + old_minute:
print(f"Amount to subtract cannot be larger then current hours + minutes ({old_hour}h {old_minute}m)")
else:
return None, n
else:
print("Input must be a number")
elif selector != "X":
print("Invalid input.")
def get_subjects_date(date_type: str, subj_id: int, db_path: str) -> str:
with closing(sqlite3.connect(db_path)) as db_con:
with closing(db_con.cursor()) as cur:
result = cur.execute(f"SELECT {date_type} FROM Subject WHERE ID = (?)", (subj_id,)).fetchone()
return result[0]
def get_name_frm_subjects_list(a_id: int, subject_list: list) -> str:
for i in subject_list:
if i[0] == a_id:
return i[1]
def correct_time_format_for_neg_minute(a_list: list, minute_num: int) -> None:
while a_list[1] < minute_num:
a_list[1] += 60
a_list[0] -= 1
def correct_time_format_for_pos_minute(a_list: list) -> None:
while a_list[1] > 59:
a_list[1] -= 60
a_list[0] += 1
def correct_time_format_display(time_list: list) -> None:
if time_list[1] < 0:
correct_time_format_for_neg_minute(time_list, 0)
else:
correct_time_format_for_pos_minute(time_list)
def check_time_tally_change(new_time_change_tuple: tuple, changed_time: dict, display_time_result: list, db_id: int, unsaved_changes: bool) -> None:
if new_time_change_tuple[0]:
if unsaved_changes:
changed_time[db_id][0] -= new_time_change_tuple[0]
else:
changed_time[db_id] = [display_time_result[0] - new_time_change_tuple[0], display_time_result[1]]
else:
if unsaved_changes:
changed_time[db_id][1] -= new_time_change_tuple[1]
else:
changed_time[db_id] = [display_time_result[0], display_time_result[1] - new_time_change_tuple[1]]
correct_time_format_for_neg_minute(changed_time[db_id], 0)
def time_data_filter(t) -> int:
return 0 if not t else t
def db_trans_change_display_setting(cur, db_id: int, name: str, new_setting: bool) -> None:
cur.execute('UPDATE Subject SET enabled = (?) WHERE ID = (?)', (1 if new_setting else 0, db_id))
log_tools.tprint(f'Saved to DB: {"Enabled" if new_setting else "Disabled"} Display for Subject Name {name} ID={db_id}')
def db_trans_change_subject_days(cur, db_id: int, name: str, new_days: dict) -> None:
days_dict = {i[0]: i[1] for i in cur.execute(f"SELECT dayCode, hours FROM SubjectDay WHERE subjectID = (?)", (db_id,)).fetchall()}
for k, v in new_days.items():
if k in days_dict.keys():
if v != days_dict[k]:
cur.execute('UPDATE SubjectDay SET hours = (?) WHERE subjectID = (?) AND dayCode = (?)', (v, db_id, k))
log_tools.tprint(f'Saved to DB: SubjectDay for Subject Name {name} ID={db_id} - changed {weekdays[int(k)]} hour from {days_dict[k]} to {v}')
else:
cur.execute('INSERT INTO SubjectDay (subjectID, dayCode, hours) VALUES (?, ?, ?)', (db_id, k, v))
log_tools.tprint(f'Saved to DB: SubjectDay for Subject Name {name} ID={db_id} - added {weekdays[int(k)]} hour - {v}')
for k in days_dict.keys():
if k not in new_days.keys():
cur.execute('DELETE FROM SubjectDay WHERE subjectID = (?) AND dayCode = (?)', (db_id, k))
log_tools.tprint(f'Saved to DB: SubjectDay for Subject Name {name} ID={db_id} - removed {weekdays[int(k)]} hours {days_dict[k]}')
def db_trans_remove_frm_time_tally(cur, timestamp: str, db_id: int, name: str, old_time_list: list, new_time_list: list, time_type: str) -> None:
time_adjustment = [0, (new_time_list[0] * 60 + new_time_list[1]) - (old_time_list[0] * 60 + old_time_list[1])]
correct_time_format_for_neg_minute(time_adjustment, -59)
cur.execute("INSERT INTO WorkLog (subjectID, logTimestamp, timeType, hour, minute, entryType) VALUES (?, ?, ?, ?, ?, 3)", (db_id, timestamp, time_type, time_adjustment[0], time_adjustment[1]))
log_tools.tprint(f'Saved to DB: WorkLog Edit ({"During Course Range" if time_type == 0 else "Outside Course Range"} Hours): Subject Name {name} ID={db_id} - {time_adjustment[0]}h {time_adjustment[1]}m')
def db_trans_change_date_range_date(cur, db_id: int, name: str, new_date: str, date_type: str) -> None:
result = cur.execute(f'SELECT {date_type} FROM Subject WHERE ID = (?)', (db_id,)).fetchone()
if result[0] != new_date:
cur.execute(f'UPDATE Subject SET {date_type} = (?) WHERE ID = (?)', (new_date, db_id))
log_tools.tprint(f"Saved to DB: Subject Name {name} ID={db_id}, changed {date_type} from {result[0]} to {new_date}")
def db_trans_change_subject_name(cur, db_id: int, old_name: str, new_name: str) -> None:
cur.execute("UPDATE Subject SET subjectName = (?) WHERE ID = (?)", (new_name, db_id))
log_tools.tprint(f"Saved to DB: Subject Name {old_name} ID={db_id} changed to {new_name}")
def edit_menu_db() -> None:
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
subjects = cur.execute("SELECT ID, subjectName FROM Subject").fetchall()
length = len(subjects)
if length > 0:
unsaved_changes = False
changed_names, changed_start_date, changed_end_date, changed_normal_time, changed_display_enable, changed_extra_time, changed_days = dict(), dict(), dict(), dict(), dict(), dict(), dict()
while 1:
n = 0
print(f"\n--EDIT SUBJECT MENU : Unsaved Changes: {unsaved_changes}--\nExit and (S)ave Changes\ne(X)it without saving")
indexes_of_changed_names = [*changed_names.keys()]
for subj in subjects:
n += 1
if subj[0] in indexes_of_changed_names:
print(f'\t{n} - {changed_names[subj[0]]}')
else:
print(f"\t{n} - {subj[1]}")
selector = input("Subject to edit:").upper()
if selector == 'X':
break
elif selector == 'S':
if unsaved_changes:
update_entry_type_list = list()
log_tools.tprint("Saving changes to Database.")
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
if changed_names:
for db_id, var in changed_names.items():
db_trans_change_subject_name(cur, db_id, get_name_frm_subjects_list(db_id, subjects), var)
changed_names.clear()
subjects = cur.execute("SELECT ID, subjectName FROM Subject").fetchall()
if changed_start_date:
for db_id, var in changed_start_date.items():
db_trans_change_date_range_date(cur, db_id, get_name_frm_subjects_list(db_id, subjects), var, "startDate")
update_entry_type_list.append(db_id)
changed_start_date.clear()
if changed_end_date:
for db_id, var in changed_end_date.items():
db_trans_change_date_range_date(cur, db_id, get_name_frm_subjects_list(db_id, subjects), var, "endDate")
if db_id not in update_entry_type_list:
update_entry_type_list.append(db_id)
changed_end_date.clear()
if changed_normal_time:
# now_time = datetime.now()
one_day_course = True # repurposed to indicate if now_time has been generated
now_time_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
for db_id, var in changed_normal_time.items():
db_trans_remove_frm_time_tally(cur, now_time_str, db_id, get_name_frm_subjects_list(db_id, subjects), [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 0", (db_id,)).fetchone()], var, 0)
changed_normal_time.clear()
if changed_extra_time:
if not one_day_course:
# now_time = datetime.now()
now_time_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
for db_id, var in changed_extra_time.items():
db_trans_remove_frm_time_tally(cur, now_time_str, db_id, get_name_frm_subjects_list(db_id, subjects), [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 1", (db_id,)).fetchone()], var, 1)
changed_extra_time.clear()
if changed_days:
for db_id, var in changed_days.items():
db_trans_change_subject_days(cur, db_id, get_name_frm_subjects_list(db_id, subjects), var)
changed_days.clear()
if changed_display_enable:
for db_id, var in changed_display_enable.items():
db_trans_change_display_setting(cur, db_id, get_name_frm_subjects_list(db_id, subjects), var)
changed_display_enable.clear()
for db_id in update_entry_type_list:
results = cur.execute("SELECT startDate, endDate FROM Subject WHERE ID = (?)", (db_id,)).fetchone()
cur.execute("UPDATE WorkLog SET timeType = IIF(logTimestamp >= (?) AND logTimestamp <= (?), 0, 1) WHERE subjectID = (?)", (results[0] + ' 00:00:00', results[1] + ' 23:59:59', db_id))
db_con.commit()
unsaved_changes = False
else:
print("No changed saved because non where found.")
elif validate_selector(selector, length + 1, 1):
selector_int = int(selector) - 1
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
if subjects[selector_int][0] in changed_start_date.keys():
start_date = changed_start_date[subjects[selector_int][0]]
else:
start_date = cur.execute("SELECT startDate FROM Subject WHERE ID = (?)", (subjects[selector_int][0],)).fetchone()[0]
if subjects[selector_int][0] in changed_end_date.keys():
end_date = changed_end_date[subjects[selector_int][0]]
else:
end_date = cur.execute("SELECT endDate FROM Subject WHERE ID = (?)", (subjects[selector_int][0],)).fetchone()[0]
if subjects[selector_int][0] in changed_normal_time.keys():
normal_time_result = changed_normal_time[subjects[selector_int][0]]
else:
normal_time_result = [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 0", (subjects[selector_int][0],)).fetchone()]
if subjects[selector_int][0] in changed_extra_time.keys():
extra_time_result = changed_extra_time[subjects[selector_int][0]]
else:
extra_time_result = [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 1", (subjects[selector_int][0],)).fetchone()]
if subjects[selector_int][0] in changed_days.keys():
days_dict = changed_days[subjects[selector_int][0]]
else:
days_dict = {i[0]: i[1] for i in cur.execute(f"SELECT dayCode, hours FROM SubjectDay WHERE subjectID = (?)", (subjects[selector_int][0],)).fetchall()}
if subjects[selector_int][0] in changed_display_enable:
display_enabled = changed_display_enable[subjects[selector_int][0]]
else:
display_enabled = True if cur.execute('SELECT enabled FROM Subject WHERE ID = (?)', (subjects[selector_int][0],)).fetchone()[0] == 1 else False
correct_time_format_display(normal_time_result)
correct_time_format_display(extra_time_result)
while 1:
if start_date == end_date:
one_day_course = True
n = 6
else:
one_day_course = False
n = 8
day_line = '\n4. Add Day\n5. Remove Day' if one_day_course is False else ''
print(f"\n--Edit Subject {changed_names[subjects[selector_int][0]] if subjects[selector_int][0] in indexes_of_changed_names else subjects[selector_int][1]}--\ne(X)it to previous menu\n(S)ave changes\n1. Name\n2. Start Date\t\t{datetime.strptime(start_date, '%Y-%m-%d').date().strftime('%d/%m/%Y')}\n3. End Date\t\t{datetime.strptime(end_date, '%Y-%m-%d').date().strftime('%d/%m/%Y')}{day_line}\n6. Remove from Normal Tally\t{normal_time_result[0]}h {normal_time_result[1]}m\n7. Remove from Extra Tally\t{extra_time_result[0]}h {extra_time_result[1]}m\n8. Display\t{'Enabled' if display_enabled else 'Disabled'}\n::Edit a Day")
for d, hour in days_dict.items():
n += 1
print(f"{n}. {weekdays[int(d)]}\t\t{hour} Hour{'' if hour == 1 else 's'} ")
selector = input("Field to edit:").upper()
if selector == 'X':
break
elif selector == 'S':
if unsaved_changes:
log_tools.tprint(f"Saving changes to {subjects[selector_int][1]} to Database")
with closing(sqlite3.connect(database_path)) as db_con:
with closing(db_con.cursor()) as cur:
this_field_unsaved_changes = False
if subjects[selector_int][0] in indexes_of_changed_names:
db_trans_change_subject_name(cur, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), changed_names[subjects[selector_int][0]])
changed_names.pop(subjects[selector_int][0])
indexes_of_changed_names.remove(subjects[selector_int][0])
subjects = cur.execute("SELECT ID, subjectName FROM Subject").fetchall()
if subjects[selector_int][0] in changed_start_date.keys():
this_field_unsaved_changes = True # var repurposed to need to change timeType column
db_trans_change_date_range_date(cur, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), changed_start_date[subjects[selector_int][0]], "startDate")
changed_start_date.pop(subjects[selector_int][0])
if subjects[selector_int][0] in changed_end_date.keys():
this_field_unsaved_changes = True
db_trans_change_date_range_date(cur, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), changed_end_date[subjects[selector_int][0]], "endDate")
changed_end_date.pop(subjects[selector_int][0])
if subjects[selector_int][0] in changed_normal_time.keys():
# now_time = datetime.now()
one_day_course = True # repurposed to indicate if now_time has been generated
now_time_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
db_trans_remove_frm_time_tally(cur, now_time_str, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 0", (subjects[selector_int][0],)).fetchone()], changed_normal_time[subjects[selector_int][0]], 0)
changed_normal_time.pop(subjects[selector_int][0])
if subjects[selector_int][0] in changed_extra_time.keys():
if not one_day_course:
# now_time = datetime.now()
now_time_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
db_trans_remove_frm_time_tally(cur, now_time_str, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), [time_data_filter(x) for x in cur.execute(f"SELECT sum(hour), sum(minute) FROM WorkLog WHERE subjectID = (?) AND timeType = 1", (subjects[selector_int][0],)).fetchone()], changed_extra_time[subjects[selector_int][0]], 1)
changed_extra_time.pop(subjects[selector_int][0])
if subjects[selector_int][0] in changed_days.keys():
db_trans_change_subject_days(cur, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), changed_days[subjects[selector_int][0]])
changed_days.pop(subjects[selector_int][0])
if subjects[selector_int][0] in changed_display_enable.keys():
db_trans_change_display_setting(cur, subjects[selector_int][0], get_name_frm_subjects_list(subjects[selector_int][0], subjects), changed_display_enable[subjects[selector_int][0]])
changed_display_enable.pop(subjects[selector_int][0])
if this_field_unsaved_changes:
results = cur.execute("SELECT startDate, endDate FROM Subject WHERE ID = (?)", (subjects[selector_int][0],)).fetchone()
cur.execute("UPDATE WorkLog SET timeType = IIF(logTimestamp >= (?) AND logTimestamp <= (?), 0, 1) WHERE subjectID = (?)", (results[0] + ' 00:00:00', results[1] + ' 23:59:59', subjects[selector_int][0]))
db_con.commit()
if not (indexes_of_changed_names or changed_start_date or changed_end_date or changed_normal_time or changed_display_enable or changed_extra_time or changed_days):
unsaved_changes = False
else:
if validate_selector(selector, n + 1):
field = int(selector)
if (not one_day_course and field > 8) or (one_day_course and field > 6):
d = [*days_dict.keys()][field - 7 if one_day_course else field - 9]
while 1:
day_name = weekdays[d]
print(f"\n--Edit {subjects[selector_int][1]} {day_name}--\n--X to exit.")
num = input("Number of hours:").upper()
if num.isdecimal():