nowfocus 0.5.7__py3-none-any.whl → 0.5.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
nowfocus/__main__.py CHANGED
@@ -5,6 +5,7 @@ import json
5
5
  import time
6
6
  from datetime import datetime, timezone, timedelta
7
7
  import importlib
8
+ from importlib.metadata import version
8
9
  from urllib.request import urlopen
9
10
  import requests
10
11
  import threading
@@ -63,7 +64,7 @@ class Application(Gtk.Application):
63
64
  try:
64
65
 
65
66
  # self.window = None # Think this is unused
66
-
67
+
67
68
  self.is_running = False
68
69
  self.device_in_use = True
69
70
  self.UserIdleTime = UserIdleTime()
@@ -84,10 +85,10 @@ class Application(Gtk.Application):
84
85
  install.run_first_load_actions()
85
86
 
86
87
  try:
87
- self.version = importlib.metadata.version(conf.app_id)
88
+ self.version = version(conf.app_id)
88
89
  print('nowfocus version', self.version)
89
90
  except Exception as e:
90
- print(e)
91
+ dbg(e=e,l=0)
91
92
 
92
93
  if get_system_db_value('db_schema_version') != self.version:
93
94
 
@@ -106,24 +107,35 @@ class Application(Gtk.Application):
106
107
  self.start_pipe()
107
108
  self.open_task_window()
108
109
 
110
+
111
+ # Have an error
112
+ # 1/0
113
+
109
114
  # Testing
110
115
  # TODO: enable these automatically with dbg system
111
116
  # self.open_session_options_dialog('test_param')
112
- # self.open_settings_window() #for testing
117
+ self.open_settings_window(page_number=3) #for testing
113
118
  # self.open_new_task_dialog() #for testing
114
119
  # self.print_time_totals()
115
120
  # self.new_task_dialog()
116
121
 
122
+ # error_dialog_inst = ErrorDialog(None,"test\n TEst")
123
+ # error_dialog_inst.show_all()
124
+
117
125
  signal.signal(signal.SIGINT, self.quit)
118
126
  signal.signal(signal.SIGUSR1, self.signal_handler)
119
127
  signal.signal(signal.SIGUSR2, self.signal_handler)
120
128
 
121
129
  Gtk.main()
130
+
122
131
  except Exception as e:
123
- print(conf.app_name +' had a pretty bad error. Please submit the following trace as an issue on the git repo or email it to the developers ')
124
- print(e)
125
- traceback.print_tb(e.__traceback__)
126
- self.quit()
132
+
133
+ dbg(notification='Total error happened', e=e, l=0)
134
+ pass
135
+ # print(conf.app_name +' had a pretty bad error. Please submit the following trace as an issue on the git repo or email it to the developers ')
136
+ # print(e)
137
+ # traceback.print_tb(e.__traceback__)
138
+ # self.quit()
127
139
 
128
140
 
129
141
  def print_time_totals(self = None, widget = None):
@@ -157,7 +169,7 @@ class Application(Gtk.Application):
157
169
  try:
158
170
  os.remove(conf.pipe)
159
171
  except Exception as e:
160
- dbd("Error removing conf.pipe in quit",e)
172
+ dbg("Error removing conf.pipe in quit",e=e)
161
173
 
162
174
  notify.uninit()
163
175
  Gtk.main_quit()
@@ -374,7 +386,7 @@ class Application(Gtk.Application):
374
386
  self.is_running = True
375
387
  dbg("resuming session",s['label'],l=2, s='session')
376
388
  except Exception as e:
377
- dbg("Error resuming session",e,l=1)
389
+ dbg("Error resuming session",e=e ,l=1)
378
390
 
379
391
 
380
392
  def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
@@ -445,7 +457,8 @@ class Application(Gtk.Application):
445
457
  self.task_running_menu_additions()
446
458
 
447
459
  self.menu.show_all()
448
- dbg("Starting",task_data['extended_label'])
460
+ dbg("Starting",task_data['extended_label'],l=-1)
461
+ dbg("new session task priority:", s['task']['priority'], l=-1)
449
462
 
450
463
  if task_data['id'] in conf.user['task_commands']:
451
464
  command_data = conf.user['task_commands'][task_data['id']]
@@ -464,7 +477,7 @@ class Application(Gtk.Application):
464
477
  process = None
465
478
  for proc in psutil.process_iter():
466
479
  pinfo = proc.as_dict(attrs=['pid', 'name'])
467
- print("Check process", pinfo['name'])
480
+ dbg("Check process", pinfo['name'], s='commands', l=3)
468
481
  if pinfo['name'] == command:
469
482
  process = proc
470
483
  break
@@ -472,16 +485,18 @@ class Application(Gtk.Application):
472
485
  continue
473
486
 
474
487
  if process:
475
- print("waiting for already running command with psutils", command)
488
+ dbg("waiting for already running command with psutils", command, s='commands')
476
489
  # subprocess.run('wmctrl', '-a', command) # Doesn't work on wayland
477
490
  process.wait()
478
491
  else:
479
- print("Launching command with subprocess.run", command)
492
+ dbg("Launching command with subprocess.run", command, s='commands')
480
493
  subprocess.run(command,shell=True)
481
494
 
482
- print("running task command complete:", command)
495
+ dbg("running task command complete:", command, s='commands')
496
+
497
+ # stop timer on command completion if duration is more an 3 seconds and session is still the same task
483
498
 
484
- if self.session['label'] == self.running_command_task_label:
499
+ if self.session['duration'] > 3 and self.session['label'] == self.running_command_task_label:
485
500
  GLib.idle_add(self.stop_task)
486
501
  GLib.idle_add(self.open_task_window)
487
502
 
@@ -510,7 +525,7 @@ class Application(Gtk.Application):
510
525
  playsound('sound/xylophone-chord.mp3',False)
511
526
 
512
527
  except Exception as e:
513
- error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure", e=e )
528
+ dbg(notification="Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure", e=e,l=0 )
514
529
 
515
530
 
516
531
 
@@ -532,16 +547,16 @@ class Application(Gtk.Application):
532
547
  # Get time tracker for this tasks todolist
533
548
  todolist_conf = conf.user['todolists'][task['todolist']]
534
549
 
535
- if 'timetracker' in todolist_conf:
550
+ if todolist_conf['timetracker']:
536
551
 
537
- dbg("Save Session to "+ todolist_conf['timetracker'])
552
+ dbg("Save Session to "+ str(todolist_conf['timetracker']))
538
553
  try:
539
554
 
540
555
  timetracker_conf = conf.user['timetrackers'][todolist_conf['timetracker']]
541
556
  save_thread = threading.Thread(target=conf.timetracker_connectors[timetracker_conf['type']].save_session, args=(session,timetracker_conf) )
542
557
  save_thread.start()
543
558
  except Exception as e:
544
- error_notice('Error Saving Time Data'," Recording timetracking for "+ task['label']+" in "+timetracker_conf['label']+" had a serious failure",e )
559
+ dbg('Error Saving Time Data',notification="Recording timetracking for "+ task['label']+" in "+timetracker_conf['label']+" had a serious failure",e=e ,l=0 )
545
560
 
546
561
  session['timetracker'] = todolist_conf['timetracker']
547
562
 
@@ -665,8 +680,9 @@ class Application(Gtk.Application):
665
680
  list_menus['recent'].append(i)
666
681
 
667
682
  except Exception as e:
683
+ pass
668
684
  # Because a key error if the task has was completed or todolist removed
669
- print('recent_tasks error', e)
685
+ dbg('recent_tasks error', e=e)
670
686
  menu.append(Gtk.SeparatorMenuItem.new())
671
687
 
672
688
 
@@ -738,12 +754,12 @@ class Application(Gtk.Application):
738
754
  self.session_options_dialog.show_all()
739
755
 
740
756
 
741
- def open_settings_window(self, w = None, **kwargs):
757
+ def open_settings_window(self, w = None, page_number=0, **kwargs):
742
758
 
743
759
  if hasattr(self, 'settings_window'):
744
760
  self.settings_window.present()
745
761
  else:
746
- self.settings_window = SettingsWindow(self,**kwargs)
762
+ self.settings_window = SettingsWindow(self, page_number, **kwargs)
747
763
  self.settings_window.show_all()
748
764
 
749
765
 
@@ -811,7 +827,7 @@ class Application(Gtk.Application):
811
827
  error_notice('Commandline Task failed','Could not find a task matching argument '+str(data))
812
828
 
813
829
  except FileNotFoundError as e:
814
- print("Error starting pip listener, CLI will not work",e)
830
+ dbg(notification="Error starting pip listener, CLI will not work",e=e,l=0)
815
831
 
816
832
  # keep listening
817
833
  self.check_pipe()
@@ -883,7 +899,7 @@ def startup():
883
899
 
884
900
 
885
901
  except Exception as e:
886
- print(f"Named pipe creation failed: {e}")
902
+ dbg("Named pipe creation failed",e=e,l=0)
887
903
 
888
904
 
889
905
  if __name__ == "__main__":
nowfocus/conf.py CHANGED
@@ -22,6 +22,7 @@ user_settings_dir = GLib.get_user_config_dir()+"/"+app_id
22
22
 
23
23
  db_file = user_data_dir+"/data.db"
24
24
  settings_file = user_data_dir+"/nowfocus-settings.json"
25
+ error_log_file = user_data_dir+"/error.log"
25
26
  debug_level = 1 # dev value
26
27
  debug_systems = []
27
28
  pipe = "/tmp/"+app_id+"-pipe"
@@ -71,7 +72,7 @@ prototype_settings = {
71
72
  'max_top_level_menu_items':10,
72
73
  'hours_search_timeframe':'this year',
73
74
  'invoice_hourly_rate':0,
74
- 'default_list_for_new_tasks':'Most recently used list',
75
+ 'default_list_for_new_tasks':'List that was last added to',
75
76
 
76
77
  "custom_pomodoro_intervals": {
77
78
  "email":7 #minutes
@@ -162,7 +163,6 @@ for key, val in prototype_settings.items():
162
163
  # print(json.dumps(user, indent=4))
163
164
  # print(json.dumps(connectors['todolists'], indent=4))
164
165
 
165
-
166
166
  with open(settings_file,"w") as file:
167
167
  json.dump(user, file)
168
168
 
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import conf
3
3
  import utils
4
+ import subprocess
4
5
  from taskw import TaskWarrior
5
6
 
6
7
  # lists and tasks are each a dicts of dicts with following shape
@@ -115,5 +116,11 @@ def get_todos(user_conf):
115
116
  todos = {'lists':lists,'tasks':tasks}
116
117
  return todos
117
118
 
119
+ def launch(user_conf):
120
+ subprocess.Popen(['x-terminal-emulator'])
121
+ # subprocess.call(['x-terminal-emulator', '-e', 'task']) # Supposed to work but doesn't
122
+ # subprocess.call(['gnome-terminal', '-x', 'task']) # Me too!
123
+
124
+
118
125
  # testing
119
126
  # print(get_todos())
@@ -0,0 +1,69 @@
1
+ import gi
2
+ gi.require_version('Gtk', '3.0')
3
+ from gi.repository import Gtk, GLib, Gdk
4
+ import conf
5
+ import utils
6
+ from utils import *
7
+
8
+
9
+ class ErrorDialog(Gtk.Dialog):
10
+
11
+ def __init__(self, parent, error_details_str, system = ''):
12
+
13
+ super().__init__(title=conf.app_name + " Error ", transient_for=parent, flags=0)
14
+ self.error_details = str(error_details_str)
15
+
16
+ print("error dialog init")
17
+
18
+ # self.app = parent.app
19
+ self.set_default_size(600,400)
20
+
21
+ self.set_border_width(15)
22
+
23
+ dialog = self.get_content_area()
24
+
25
+ dialog.set_spacing(15)
26
+
27
+ intro_text = conf.app_name+" "+system+" has hit a bump in the road..."
28
+
29
+ intro = Gtk.Label(intro_text)
30
+
31
+ dialog.add(intro)
32
+
33
+ box = Gtk.Box()
34
+ error_label = Gtk.Label(self.error_details)
35
+ box.add(error_label)
36
+
37
+ scrolled_window = Gtk.ScrolledWindow()
38
+ scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
39
+ # scrolled_window.set_size_request(-1, 350)
40
+
41
+ scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
42
+ scrolled_window.add(box)
43
+
44
+ dialog.pack_end(scrolled_window,1,1,True)
45
+
46
+ # dialog.add(Gtk.Box(border_width=10)) #spacer
47
+
48
+ self.add_button("Okay Whatever", 1)
49
+
50
+ self.add_button("Send to developers", 2)
51
+
52
+ self.set_default_response(2)
53
+
54
+ self.show_all()
55
+ self.connect("response", self.on_response,)
56
+
57
+ def on_response(self, widget, response):
58
+ if response == 2:
59
+
60
+ import subprocess
61
+
62
+ subprocess.run([
63
+ "xdg-email",
64
+ "--subject", conf.app_name+" Error Report",
65
+ "--body", self.error_details,
66
+ # "recipient@example.com"
67
+ ])
68
+
69
+ self.destroy()
@@ -43,7 +43,7 @@ class NewTaskWDialog(Gtk.Dialog):
43
43
 
44
44
  box.add(Gtk.Box(border_width=10)) #spacer
45
45
 
46
- default_list = get_most_recent_list(self.app.session)
46
+ default_list = get_default_list_for_new_tasks()
47
47
 
48
48
  box.add(choose_from_lists(self.select_list, default_list['id']))
49
49
  self.selected_list = default_list
@@ -97,6 +97,9 @@ class NewTaskWDialog(Gtk.Dialog):
97
97
  # add it to the database
98
98
  db_set_item(task)
99
99
 
100
+ # Add list and last appended to list in system db
101
+ set_system_db_value('last_added_to_list_id',self.selected_list['id'])
102
+
100
103
  # add it to the menu
101
104
  menu_tasks[task['id']] = Gtk.MenuItem.new_with_label(task['label'])
102
105
  menu_tasks[task['id']].connect("activate", self.app.start_task, task)
@@ -122,7 +125,7 @@ class NewTaskWDialog(Gtk.Dialog):
122
125
  if conf.debug_level > 1:
123
126
  raise e
124
127
 
125
- error_notice('Error adding tasks',"Adding "+ task_label+" to "+todolist_conf['label']+" had a serious failure",e) # NOTE: e, in the case of a key error, only prints the bad key, not the error type
128
+ dbg(notification="Error adding tasks "+str(task_label)+" to "+todolist_conf['label'], e=e,l=0)
126
129
 
127
130
 
128
131
  elif response == Gtk.ResponseType.CANCEL:
@@ -32,9 +32,10 @@ class SessionEditDialog(Gtk.Dialog):
32
32
  else:
33
33
  error_notice("Error: SessionEditDialog called without session data")
34
34
 
35
- print(' SessionEditDialog target_session',target_session)
35
+ dbg('SessionEditDialog target_session',target_session,l=-1)
36
36
 
37
37
  self.s['task'] = db_get_item_by_id(self.s['task_id'])
38
+
38
39
  # Used to identify and remove old session
39
40
  self.session_id = {
40
41
  'extended_label':self.s['extended_label'], 'start_time': self.s['start_time']
@@ -47,7 +48,6 @@ class SessionEditDialog(Gtk.Dialog):
47
48
 
48
49
  box = self.get_content_area()
49
50
 
50
-
51
51
  # label = Gtk.Label()
52
52
  # label.set_markup("<b>"+self.s['extended_label']+"</b> ")
53
53
  # box.add(label)
@@ -160,8 +160,6 @@ class SessionEditDialog(Gtk.Dialog):
160
160
  if response == 2:
161
161
  db_query("DELETE FROM sessions WHERE extended_label = ? AND start_time = ? LIMIT 1",(self.session_id['extended_label'],self.session_id['start_time']))
162
162
 
163
- error_notice('Session Deleted','')
164
-
165
163
  if response == 3:
166
164
 
167
165
  if self.notes_text_buffer.get_modified():
nowfocus/settings.py CHANGED
@@ -94,11 +94,13 @@ class SettingsWindow(Gtk.Window):
94
94
 
95
95
 
96
96
 
97
- def __init__(self, parent=None, **kwargs):
97
+ def __init__(self, parent=None, page_number=0, **kwargs):
98
98
  self.app = parent #NOTE: This doesn't look like it will work when called from task_window...
99
99
  Gtk.Window.__init__(self, title="Settings")
100
- self.set_border_width(15)
100
+ self.set_border_width(0)
101
101
  self.set_position(position=1)
102
+ self.set_default_size(-1,800)
103
+
102
104
  self.connect("destroy", self.on_close)
103
105
 
104
106
  self.notebook = Gtk.Notebook()
@@ -106,15 +108,17 @@ class SettingsWindow(Gtk.Window):
106
108
 
107
109
 
108
110
 
109
-
110
-
111
- # Settings
111
+ # Settings page
112
112
  row = 0
113
113
  grid = Gtk.Grid()
114
114
  grid.set_row_spacing(10)
115
115
  grid.set_column_spacing(10)
116
- self.notebook.append_page(grid,Gtk.Label('Settings'))
116
+ grid.set_border_width(15)
117
+ settings_page = Gtk.Box()
118
+ settings_scroller = self.scroll_box(settings_page)
119
+ settings_scroller.add(grid)
117
120
 
121
+ self.notebook.append_page(settings_page,Gtk.Label('Settings'))
118
122
 
119
123
  grid.attach(Gtk.Box(border_width=10),0,(row:=row+1),5,1) # Spacer
120
124
 
@@ -129,9 +133,11 @@ class SettingsWindow(Gtk.Window):
129
133
 
130
134
  grid.attach(self.settings_updater('todolist_refresh_interval', 3, 'SpinButton',"(hours)"),0,(row:=row+1),5,1)
131
135
 
132
-
133
136
  # grid.attach(self.settings_updater('invoice_hourly_rate', 33, 'SpinButton',""),0,(row:=row+1),5,1)
134
137
 
138
+ grid.attach(self.settings_updater('default_list_for_new_tasks', 'List that was last added to', 'ComboBoxText',"", options = ['List that was last added to','Most recently used list']),0,(row:=row+1),5,1)
139
+
140
+
135
141
  grid.attach(self.settings_updater('hours_search_timeframe', 'auto', 'ComboBoxText',"\nDefault timetracking range for hours shown in main window and invoicing", options = list(hours_search_timeframes().keys())),0,(row:=row+1),5,1)
136
142
 
137
143
  grid.attach(self.settings_updater('prompts', None, 'TextView',""),0,(row:=row+1),5,1)
@@ -143,13 +149,18 @@ class SettingsWindow(Gtk.Window):
143
149
 
144
150
 
145
151
 
152
+
153
+ connectors_page = Gtk.Box()
154
+ self.notebook.append_page(connectors_page,Gtk.Label('Todolists and Time Trackers'))
155
+ connectors_scroller = self.scroll_box(connectors_page)
156
+
157
+ # TODO: perhaps use a scroller for each connector category instead of one big one
146
158
  row = 0
147
159
  grid = Gtk.Grid()
160
+ connectors_scroller.add(grid)
148
161
  grid.set_row_spacing(10)
149
162
  grid.set_column_spacing(10)
150
- grid.set_border_width(10)
151
-
152
- self.notebook.append_page(grid,Gtk.Label('Todolists and Time Trackers'))
163
+ grid.set_border_width(15)
153
164
 
154
165
  sub_head = Gtk.Label()
155
166
  sub_head.set_markup("<b>Todo Lists</b>")
@@ -227,11 +238,13 @@ class SettingsWindow(Gtk.Window):
227
238
 
228
239
 
229
240
  # Task Commands notebook page
230
- self.task_commands_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
231
- self.notebook.append_page(self.task_commands_box,Gtk.Label('Task Commands'))
241
+ self.task_commands_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
242
+ self.notebook.append_page(self.task_commands_page,Gtk.Label('Task Commands'))
243
+
244
+ self.task_commands_box = self.scroll_box(self.task_commands_page)
245
+ self.task_commands_box.set_border_width(15)
232
246
 
233
247
  self.boxes['task_commands_outer'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
234
- # self.boxes['task_commands_outer'] = self.scroll_box()
235
248
  self.task_commands_box.add(self.boxes['task_commands_outer'])
236
249
 
237
250
  # Gets replaced when commands are loaded
@@ -248,19 +261,22 @@ class SettingsWindow(Gtk.Window):
248
261
 
249
262
 
250
263
 
264
+
265
+
266
+
251
267
  # Time Targets notebook page
252
- self.targets_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
253
- self.notebook.append_page(self.targets_box, Gtk.Label('Time Targets'))
268
+ self.targets_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
254
269
 
255
- self.boxes['lists_time_targets_outer'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
256
- # self.targets_box.add(self.boxes['lists_time_targets_outer'])
270
+ self.notebook.append_page(self.targets_page, Gtk.Label('Time Targets'))
257
271
 
258
-
259
- # self.boxes['test'] = self.scroll_box(None ,300)
260
- # self.targets_box.add(self.boxes['test'])
272
+ self.targets_scroller = self.scroll_box(self.targets_page ,300)
261
273
 
262
- # self.boxes['lists_time_targets_outer'] = self.scroll_box(None ,300)
274
+ self.targets_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
275
+ self.targets_box.set_border_width(15)
276
+
277
+ self.targets_scroller.add(self.targets_box)
263
278
 
279
+ self.boxes['lists_time_targets_outer'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
264
280
 
265
281
  self.targets_box.add(self.boxes['lists_time_targets_outer'])
266
282
 
@@ -268,13 +284,6 @@ class SettingsWindow(Gtk.Window):
268
284
  # Gets replaced when targets are loaded
269
285
  self.boxes['lists_time_targets_inner'] = Gtk.Box()
270
286
  self.boxes['lists_time_targets_outer'].add(self.boxes['lists_time_targets_inner'])
271
-
272
- # separator = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL)
273
- # self.boxes['lists_time_targets_outer'].pack_start(separator, True, True, 0)
274
-
275
- # test_button = Gtk.Button(label="test")
276
- # self.boxes['lists_time_targets_inner'].add(test_button)
277
-
278
287
 
279
288
  self.show_time_targets('lists')
280
289
 
@@ -286,6 +295,7 @@ class SettingsWindow(Gtk.Window):
286
295
 
287
296
  self.boxes['tasks_time_targets_outer'] = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
288
297
  self.targets_box.add(self.boxes['tasks_time_targets_outer'])
298
+
289
299
  # Gets replaced when targets are loaded
290
300
  self.boxes['tasks_time_targets_inner'] = Gtk.Box()
291
301
  self.boxes['tasks_time_targets_outer'].add(self.boxes['tasks_time_targets_inner'])
@@ -308,6 +318,7 @@ class SettingsWindow(Gtk.Window):
308
318
 
309
319
  # Sessions notebook page
310
320
  self.sessions_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
321
+ self.sessions_page.set_border_width(15)
311
322
 
312
323
  self.notebook.append_page(self.sessions_page,Gtk.Label('Sessions'))
313
324
 
@@ -336,8 +347,9 @@ class SettingsWindow(Gtk.Window):
336
347
 
337
348
  self.SessionEditDialog = SessionEditDialog # passed to show_sessions
338
349
 
339
-
350
+ print('set_current_page',page_number)
340
351
  self.show_all()
352
+ self.notebook.set_current_page(page_number)
341
353
 
342
354
 
343
355
 
@@ -434,7 +446,7 @@ class SettingsWindow(Gtk.Window):
434
446
  checkbutton.set_label("Error: '"+str(id)+"' didn't match anything " )
435
447
  else:
436
448
  # print('Show time target',item_data)
437
- checkbutton.set_label(item_data['label']+": "+str(data['type'])+ " "+str(data['value'])+" minutes per "+str(data['within_value'])+" "+str(data['within_unit']) )
449
+ checkbutton.set_label(item_data['label']+" \n"+str(data['type'])+ " "+str(data['value'])+" minutes per "+str(data['within_value'])+" "+str(data['within_unit']) )
438
450
  checkbutton.set_active(data['status'])
439
451
 
440
452
  # checkbutton.connect("toggled", print, checkbutton.get_active())
@@ -476,9 +488,9 @@ class SettingsWindow(Gtk.Window):
476
488
 
477
489
  task_data = db_get_item_by_id(id,"tasks")
478
490
  if not task_data:
479
- checkbutton.set_label("Error: '"+str(id)+"' task missing "+str(command_data['command']))
491
+ checkbutton.set_label("Error: '"+str(id)+"' task missing \n"+str(command_data['command']))
480
492
  else:
481
- checkbutton.set_label(task_data['extended_label']+" "+str(command_data['command']) )
493
+ checkbutton.set_label(task_data['extended_label']+" \n"+str(command_data['command']) )
482
494
  checkbutton.set_active(command_data['status'])
483
495
 
484
496
  checkbutton.connect("toggled", update_user_setting, conf.user['task_commands'][id], 'status', checkbutton.get_active)
nowfocus/task_window.py CHANGED
@@ -36,7 +36,7 @@ class TaskWindow(Gtk.Window):
36
36
  # TaskWindow._instance.present()
37
37
  # return None
38
38
  except Exception as e:
39
- dbg("TaskWindow._instance exception",e,s='taskwindow')
39
+ dbg("TaskWindow._instance exception",e=e ,s='taskwindow')
40
40
 
41
41
  TaskWindow._instance = self
42
42
 
@@ -197,7 +197,7 @@ class TaskWindow(Gtk.Window):
197
197
  self.buttons_box.add(openable_button)
198
198
 
199
199
  except Exception as e:
200
- dbg("error adding todo connector open() to task window ",e, s='taskwindow')
200
+ dbg("error adding todo connector open() to task window ",e=e , s='taskwindow')
201
201
 
202
202
  self.new_task_button = Gtk.Button(label="New Task")
203
203
  self.new_task_button.set_property("tooltip-text","Add a new task (Ctrl + N)")
@@ -521,7 +521,7 @@ class TaskWindow(Gtk.Window):
521
521
 
522
522
 
523
523
  except Exception as e:
524
- utils.dbg("Error adding task to list"+ str(t['extended_label']), t, e, l=0, s='taskwindow')
524
+ utils.dbg("Error adding task to list"+ str(t['extended_label']), t, e=e, l=0, s='taskwindow')
525
525
  return False
526
526
 
527
527
 
nowfocus/upgrade.py CHANGED
@@ -37,14 +37,7 @@ def do_upgrades(app):
37
37
 
38
38
  db_query("ALTER TABLE sessions ADD COLUMN priority INTEGER DEFAULT 0")
39
39
 
40
- for session in db_query("SELECT DISTINCT task_id, extended_label FROM sessions"):
41
- t = db_get_item_by_id(session['task_id'],dgb_error_level_for_failure=3)
42
- if t and t['priority'] > 0:
43
- print("setting",session['extended_label']," session priority to ",t['priority'] )
44
40
 
45
- db_query("UPDATE sessions SET priority = ? WHERE task_id = ?",(t['priority'],session['task_id']))
46
-
47
- print("Total hours priority sessions",round(divide(db_query("SELECT SUM(duration) as total FROM sessions WHERE priority > 0")[0]['total'],3600),2))
48
41
  db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.5')")
49
42
 
50
43
  db_schema_version = '0.5'
@@ -58,6 +51,20 @@ def do_upgrades(app):
58
51
  db_schema_version = "0.5.6"
59
52
  set_system_db_value("db_schema_version",db_schema_version)
60
53
 
54
+ if db_schema_version == '0.5.6':
55
+ dbg('Schema Update from', db_schema_version, 'to 0.5.9',l=-1)
56
+
57
+ for session in db_query("SELECT DISTINCT task_id, extended_label FROM sessions"):
58
+ t = db_get_item_by_id(session['task_id'],dgb_error_level_for_failure=3)
59
+ if t and t['priority'] > 0:
60
+ print("setting",session['extended_label']," session priority to ",t['priority'] )
61
+
62
+ db_query("UPDATE sessions SET priority = ? WHERE task_id = ?",(t['priority'],session['task_id']))
63
+
64
+ print("Total hours priority sessions",round(divide(db_query("SELECT SUM(duration) as total FROM sessions WHERE priority > 0")[0]['total'],3600),2))
65
+
66
+ db_schema_version = "0.5.9"
67
+ set_system_db_value("db_schema_version",db_schema_version)
61
68
  # if db_schema_version == 0.5:
62
69
  # db_query("ALTER TABLE tasks ADD COLUMN tags TEXT DEFAULT '{}'")
63
70
  # db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.5')")
nowfocus/utils.py CHANGED
@@ -26,6 +26,8 @@ import traceback
26
26
  import conf
27
27
  notify.init(conf.app_name)
28
28
 
29
+ from error_dialog import ErrorDialog
30
+
29
31
  lists = {}
30
32
 
31
33
 
@@ -66,8 +68,24 @@ def dbg(*data, s="", l=2, e=None, notification=None):
66
68
  # pretty_print(data)
67
69
 
68
70
  if e and isinstance(e,Exception):
69
- traceback.print_tb(e.__traceback__)
70
- print(e,"\n")
71
+
72
+ error_report = str(e) + '\n' + traceback.format_exc()
73
+
74
+ print(error_report)
75
+
76
+ if data:
77
+ error_report = json.dumps(data,indent=2) + error_report
78
+
79
+ print("show ErrorDialog")
80
+ error_dialog_inst = ErrorDialog(None, error_report)
81
+ error_dialog_inst.show_all()
82
+
83
+ # if level == 0:
84
+ # with open(conf.error_log_file,'a') as error_log:
85
+ # error_log.write(str(e))
86
+ # error_log.write(traceback.format_exc())
87
+
88
+
71
89
 
72
90
  def error_notice(title, details = None, e = None):
73
91
  print('ERROR',title,details,e)
@@ -134,7 +152,7 @@ def sec_to_time(sec):
134
152
  try:
135
153
  int(sec)
136
154
  except Exception as e:
137
- print("sec to time error", e)
155
+ dbg("sec to time error", e=e, l=0)
138
156
  sec = 0
139
157
 
140
158
  time = str("{:02d}".format(int(sec // 3600))) + ':' + str("{:02d}".format(int((sec % 3600) // 60))) + ':' + str("{:02d}".format(int(sec % 60)))
@@ -169,7 +187,7 @@ def validate_start_time_str(start_time_string):
169
187
  return datetime.strptime(start_time_string,'%Y-%m-%d %H:%M:%S')
170
188
 
171
189
  except Exception as e:
172
- error_notice('Incorrect Start Time Format', start_time_string+" does no match %Y-%m-%d %H:%M:%S", e)
190
+ dbg('Incorrect Start Time Format', start_time_string+" does no match %Y-%m-%d %H:%M:%S", e=e, l=0)
173
191
  return False
174
192
 
175
193
 
@@ -185,7 +203,7 @@ def open_todo(w=None, i=None, item_type = 'tasks'):
185
203
 
186
204
  except Exception as e:
187
205
  # error_notice('Bonk', "error with "+ c['type']+ " open function ")
188
- dbg('open_todo excption, falling back to get_connector_openable ',e,s=todo['type'],l=1)
206
+ dbg('open_todo exception, falling back to get_connector_openable ',e=e ,s=todo['type'],l=1)
189
207
 
190
208
  get_connector_openable(None,todo)
191
209
 
@@ -266,7 +284,7 @@ def extended_label(i):
266
284
  o = ' > '.join(l)
267
285
  return o
268
286
 
269
- # TODO cleanup this
287
+
270
288
  def lists_cache(new_lists = None, reset = False):
271
289
  '''Set or get a dict of (all) lists. If new_lists is supplied it will exclusively use that (until it is replaced or the global lists var is emptied) This is useful (because it can be used wih input when the values are not yet available in the databse) but wonky. '''
272
290
 
@@ -366,8 +384,7 @@ def reindex_all():
366
384
 
367
385
 
368
386
  set_system_db_value('taskindex_update_time', now().strftime("%Y-%m-%d %H:%M:%S"))
369
- # db_query("REPLACE INTO system(field, value) VALUES(?,?)",('taskindex_update_time',now().strftime("%Y-%m-%d %H:%M:%S")))
370
- # print('reindex done')
387
+
371
388
  timeit()
372
389
 
373
390
  return True
@@ -384,8 +401,7 @@ def reindex_one(t):
384
401
  db_query("INSERT INTO taskindex(id, extended_label, priority, status) SELECT id, extended_label, ?, status FROM tasks WHERE id = ? ",(time_target_priority(t),t['id'],))
385
402
 
386
403
  dbg("reindexed",t['label'])
387
- # print(db_query("SELECT * FROM taskindex where id = ?",(t['id'],)))
388
- # db_query("REPLACE INTO system(field, value) VALUES(?,?)",('taskindex_update_time',now().strftime("%Y-%m-%d %H:%M:%S")))
404
+
389
405
  set_system_db_value('taskindex_update_time', now().strftime("%Y-%m-%d %H:%M:%S"))
390
406
 
391
407
  return True
@@ -393,10 +409,10 @@ def reindex_one(t):
393
409
 
394
410
  def taskindex_updated_time():
395
411
  ''' returns system db taskindex_update_time string formatted as %Y-%m-%d %H:%M:%S '''
396
- get_system_db_value('taskindex_update_time')
397
-
398
- update_time = db_query("SELECT value FROM system WHERE field = 'taskindex_update_time'")[0]['value']
399
412
 
413
+ update_time = get_system_db_value('taskindex_update_time')
414
+
415
+
400
416
  # print('taskindex_updated_time',update_time)
401
417
  # return datetime.strptime(update_time,'%Y-%m-%d %H:%M:%S')
402
418
  return update_time
@@ -654,7 +670,9 @@ def db_get_item_by_id(id, table = 'tasks',dgb_error_level_for_failure=0):
654
670
  return {}
655
671
 
656
672
  except Exception as e:
657
- dbg('db_get_item_by_id failed',e,l=dgb_error_level_for_failure)
673
+ dbg('db_get_item_by_id failed',e=e ,l=dgb_error_level_for_failure)
674
+ return {}
675
+
658
676
 
659
677
 
660
678
  def get_todo_by_id(todo_or_todo_id = None):
@@ -704,8 +722,12 @@ def db_save_session(session):
704
722
  'priority': "0",
705
723
  }
706
724
 
725
+ # dbg('task in db_save_session', session['task'],l=3)
726
+
707
727
  if 'priority' in session['task']:
708
- prepared_session['priority']: str(session['task']['priority'])
728
+ prepared_session['priority'] = str(session['task']['priority'])
729
+
730
+ dbg('db_save_session data:',prepared_session)
709
731
 
710
732
  db_query("INSERT INTO sessions(start_time, duration, task_id, parent_id, todolist, extended_label,timetracker, notes, priority) VALUES(:start_time, :duration, :task_id, :parent_id, :todolist, :extended_label, :timetracker, :notes, :priority )",prepared_session)
711
733
 
@@ -792,7 +814,6 @@ def db_set_session_cache(s):
792
814
  ''' Add active session to system db table. Not to be confused with db_save_session '''
793
815
  db_session = copy.deepcopy(s)
794
816
  db_session['start_time'] = db_session['start_time'].strftime("%Y-%m-%d %H:%M:%S.%f%z")
795
-
796
817
  set_system_db_value('session', json.dumps(db_session))
797
818
 
798
819
 
@@ -995,7 +1016,7 @@ def refresh_todolist(todo, catch_errors = False):
995
1016
 
996
1017
  except Exception as e:
997
1018
  if catch_errors:
998
- dbb(notify='Error Loading '+todo['label']+' i refresh_todolist',e=e)
1019
+ dbg(notification='Error Loading '+todo['label']+' i refresh_todolist',e=e,l=0)
999
1020
  todos = {'lists': {}, 'tasks':{}}
1000
1021
 
1001
1022
  else:
@@ -1016,13 +1037,13 @@ def handle_todo_read_error(todo_conf,e):
1016
1037
  if isinstance(e, (FileExistsError, FileNotFoundError, PermissionError)):
1017
1038
  handle_todo_file_access_error(todo_conf,e)
1018
1039
 
1019
- dbg(todo_conf['label']+ " Access Error",e,l=0,notification=True)
1040
+ dbg(notification=todo_conf['label']+ " Access Error",e=e ,l=0)
1020
1041
 
1021
1042
 
1022
1043
  def handle_todo_file_read_error(todo_conf,e):
1023
1044
  conf.user['todolists'][todo_conf['id']]['status'] = False
1024
1045
  save_user_settings()
1025
- dbg(todo_conf['label']+ " Deactivated",e,l=-1,notification=True)
1046
+ dbg(notification=todo_conf['label']+ " Deactivated",e=e ,l=-1)
1026
1047
 
1027
1048
 
1028
1049
  def get_todolists(use_db_cache = False):
@@ -1058,7 +1079,7 @@ def get_todolists(use_db_cache = False):
1058
1079
  try:
1059
1080
  todos = db_get_todolist(todo['id'])
1060
1081
  except Exception as e:
1061
- error_notice('Also Failed to load '+todo['label']+' From cache' ,e=e)
1082
+ dbg('Also Failed to load '+todo['label']+' From cache' ,e=e,l=0)
1062
1083
  todos = {'lists': {}, 'tasks':{}}
1063
1084
 
1064
1085
  tasks.update(todos['tasks'])
@@ -1087,7 +1108,7 @@ def get_system_db_value(field, json = False):
1087
1108
  o = json.loads(o)
1088
1109
  return o
1089
1110
  except Exception as e:
1090
- dbg("Error getting",field, 'from system db',e,l=0)
1111
+ dbg("Error getting",field, 'from system db',e=e ,l=0)
1091
1112
 
1092
1113
 
1093
1114
  def set_system_db_value(field, value, json = False):
@@ -1098,7 +1119,7 @@ def set_system_db_value(field, value, json = False):
1098
1119
  db_query("REPLACE INTO system(field, value) VALUES(:field, :value)", {'field':field,'value':value})
1099
1120
 
1100
1121
  except Exception as e:
1101
- dbg("Error setting", field, value, 'in system db',e,l=0)
1122
+ dbg("Error setting", field, value, 'in system db',e=e ,l=0)
1102
1123
 
1103
1124
 
1104
1125
  def get_session_from_system_db():
@@ -1110,7 +1131,7 @@ def get_session_from_system_db():
1110
1131
  s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
1111
1132
  return s
1112
1133
  except Exception as e:
1113
- dbg("Error in get_session_from_system_db session",e,l=1)
1134
+ dbg("Error in get_session_from_system_db session",e=e ,l=1)
1114
1135
 
1115
1136
 
1116
1137
  def get_most_recent_list(session = None):
@@ -1139,11 +1160,14 @@ def get_most_recent_list(session = None):
1139
1160
 
1140
1161
 
1141
1162
  def get_default_list_for_new_tasks():
1142
- # if conf.user['default_list_for_new_tasks'] != 'Most recently used list':
1143
- # try:
1144
1163
 
1145
- # List that was last added to
1164
+ if conf.user['default_list_for_new_tasks'] == 'List that was last added to':
1165
+
1166
+ id = get_system_db_value('last_added_to_list_id')
1167
+ if id and id in lists_cache():
1168
+ return lists_cache()[id]
1146
1169
 
1170
+ # if conf.user['default_list_for_new_tasks'] == 'Most recently used list':
1147
1171
  return get_most_recent_list()
1148
1172
 
1149
1173
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nowfocus
3
- Version: 0.5.7
3
+ Version: 0.5.9
4
4
  Summary: nowfocus: the open source task-tracking self-control panel.
5
5
  Author: AltruistEnterprises
6
6
  Project-URL: Homepage, https://www.nowfocus.org
@@ -121,12 +121,7 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
121
121
  ```
122
122
  bash -c "echo 'open_task_window' > /tmp/nowfocus-pipe"
123
123
  ```
124
-
125
-
126
- 3. Add the following command to your startup applications:
127
- ```
128
- nowfocus --force
129
- ```
124
+
130
125
 
131
126
 
132
127
 
@@ -1,23 +1,24 @@
1
1
  nowfocus/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- nowfocus/__main__.py,sha256=1WffOUaxlfw0gc_qWY9K78j35AmoxoDDLePoK3wFlE0,33433
3
- nowfocus/conf.py,sha256=czmeMU7r5DD5fT1cDi2kjwYa5xQSXJoQoeQYsoFMkzc,6757
2
+ nowfocus/__main__.py,sha256=BDNlrtNuQWY4-PxVHKJE1yIyeMTZDQni0OUnWF9uOuk,34069
3
+ nowfocus/conf.py,sha256=fBKbM57teU4-LGcX-hmgnjH5TaP5hwQ7YhmtugDNx74,6804
4
+ nowfocus/error_dialog.py,sha256=DMMGoaqMmcTD6YZT5X6exocEs4sTveCYu03Qgc9fVP8,1799
4
5
  nowfocus/example-todo.txt,sha256=o-ZRNiTlSGFbTK9jpdDIi07qHBrpvP0JhBZSk0VlpQU,246
5
6
  nowfocus/install.py,sha256=gzjDzqUFYRP1dS_zA_MKrTZWTAB1bYqcxjRw5mBn1aQ,3039
6
- nowfocus/new_task_dialog.py,sha256=LwvwNlCg_rl0tHwLPro0Jx1p4J_ukMNM2nLQ36vUJPI,4429
7
- nowfocus/session_edit_dialog.py,sha256=V2QWSdNaxsQHRcG28CJBQM2sa45m5RNcu_suQF26mkM,6912
7
+ nowfocus/new_task_dialog.py,sha256=TtN0Cr2x3uZ62NvXrxeE1_oVqjQM5OCUY104-TSb-6A,4474
8
+ nowfocus/session_edit_dialog.py,sha256=7gayBGyZc2JlVMnmibVfHt6HL52CXErSoDDgbnaE6Zs,6858
8
9
  nowfocus/session_options.py,sha256=mOnyEM3-usKgVvBhESY0TNBcy4FTG-AHP6ETMgB1fQ4,5071
9
10
  nowfocus/sessions.csv,sha256=kYpr06yQg_J86NQ4AiYw4RnQchcw3ouPKVYa1lYDUNo,39
10
- nowfocus/settings.py,sha256=0d6nZw1t36TjyUr9KQ_wDwuh2SB64gUkZeHEjEV867Y,35169
11
+ nowfocus/settings.py,sha256=rb4kotRJLbNgPSMaN1EfhJzJ0F4xBCI_MZf30SlMazs,35796
11
12
  nowfocus/styles.css,sha256=PG1SrLkwSSay8M2VKeRcE0UdK54ndsEDFnRLRkmP-9M,510
12
- nowfocus/task_window.py,sha256=f6eaxrFwI5POJG2tZX0H3AKFA6dGt_RH42y2ydHqosY,28875
13
- nowfocus/upgrade.py,sha256=S0nyWmVLnukWY0QhC5873HFQZpcWuZ_XEPU4QTBtX3s,3379
13
+ nowfocus/task_window.py,sha256=zcR1kk2f_vtwXiIm4PY_sKZ5wsfhtEfg9Hl8X9H0TRM,28883
14
+ nowfocus/upgrade.py,sha256=ckCsXnAO8goLNZLvBWmhFQeM0sDJ_bsn5EAV3vsLiwY,3600
14
15
  nowfocus/user_idle_time.py,sha256=I44Ip-iGGzWAiHnM2_06jNqhCne9y1SbvcBI-nYBolU,2365
15
- nowfocus/utils.py,sha256=tVa4rJf0QKhDU5as4-BSIFvuZLMsxGScJkxGRYibtzA,49187
16
+ nowfocus/utils.py,sha256=s187QkyJrD8lrlsFLW1OV9TV0tK9-eOi_gVwvn6W86U,49520
16
17
  nowfocus/connectors/activitywatch.py,sha256=QbkOmjIOiVwccWc2xhhePd0Abww5vEiVpCNjeqOyYGg,921
17
18
  nowfocus/connectors/caldav.py,sha256=PeM_9yJC8W17L8Y5AyS75o6GfzTrPoMYKIvetND8T78,5089
18
19
  nowfocus/connectors/csv.py,sha256=FwMpHM5lPIT90HKBCQUncpaW7zqFjlHjMwKR0-XWg-4,821
19
20
  nowfocus/connectors/psc_timetracker.py,sha256=gyx0bQkOC467lkF7tTcoKD451u3WPEEBjAvNeZc6W5s,9767
20
- nowfocus/connectors/taskwarrior.py,sha256=bs1h5lxPxruNb6Pqf6HP-Do29i6NUdbBVDp_D5s06Ps,3172
21
+ nowfocus/connectors/taskwarrior.py,sha256=hDhbiP7tiIzK1QJCSlcA3msrsSCA6MQtJ6TO1VHsDLM,3423
21
22
  nowfocus/connectors/timewarrior.py,sha256=0Hra0GVPYdRqGtG_TbH3gzfUl192hH1DO2_WrDdrACM,698
22
23
  nowfocus/connectors/todo_template.py,sha256=R37fA2LXo8_LpWIgqozytI5RqIUjGggFHup25xTykII,1572
23
24
  nowfocus/connectors/todotxt.py,sha256=QCZjbIhY4Lm37YD0GsKJQUqbj7s3eYmZGgRwUCndZ5w,3857
@@ -52,9 +53,9 @@ nowfocus/icon/settings.svg,sha256=fgkGJouPPtZLxZn2nr_5pEp9MdhRSRaW9mtdxhJHDuQ,39
52
53
  nowfocus/sound/bell-xylophone-g.mp3,sha256=1OBcRWvD87AGNcq1uZFR8HqG0nanJykImERfVDVxHD4,53891
53
54
  nowfocus/sound/dinner-bell.mp3,sha256=hjjO0xqA4uXpYw9KLwwlBnrVfRhVq1K5OXzwlMXhRn4,113620
54
55
  nowfocus/sound/xylophone-chord.mp3,sha256=gwgBSqhMt5PMzT5N03Z6TvDgipQZfnkEz_o81Rq5Z1U,131806
55
- nowfocus-0.5.7.dist-info/licenses/LICENSE,sha256=fSJzoHs1EOCwEd7FIyokFeGEma7NKmTVEdHkCr5OIV4,35127
56
- nowfocus-0.5.7.dist-info/METADATA,sha256=HpS443Yz3_NPDZB03_G51VF4Yil7UNC4xtl8R1XqttA,6805
57
- nowfocus-0.5.7.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
58
- nowfocus-0.5.7.dist-info/entry_points.txt,sha256=RbYY19-irSoNVglNeNnL9D36cHft7aKsaEGEYoSH3pA,51
59
- nowfocus-0.5.7.dist-info/top_level.txt,sha256=3uLd9BwmfarZwqVUxkSJuVwJ8qHzjThte8rt_UYG7tE,9
60
- nowfocus-0.5.7.dist-info/RECORD,,
56
+ nowfocus-0.5.9.dist-info/licenses/LICENSE,sha256=fSJzoHs1EOCwEd7FIyokFeGEma7NKmTVEdHkCr5OIV4,35127
57
+ nowfocus-0.5.9.dist-info/METADATA,sha256=TBWJV37J-P1zGbLQlDAMB_G-ejxuSxsdufG7wupsem8,6707
58
+ nowfocus-0.5.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
59
+ nowfocus-0.5.9.dist-info/entry_points.txt,sha256=RbYY19-irSoNVglNeNnL9D36cHft7aKsaEGEYoSH3pA,51
60
+ nowfocus-0.5.9.dist-info/top_level.txt,sha256=3uLd9BwmfarZwqVUxkSJuVwJ8qHzjThte8rt_UYG7tE,9
61
+ nowfocus-0.5.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5