nowfocus 0.5.5__py3-none-any.whl → 0.5.7__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
@@ -62,7 +62,7 @@ class Application(Gtk.Application):
62
62
 
63
63
  try:
64
64
 
65
- self.window = None
65
+ # self.window = None # Think this is unused
66
66
 
67
67
  self.is_running = False
68
68
  self.device_in_use = True
@@ -83,7 +83,17 @@ class Application(Gtk.Application):
83
83
  import install
84
84
  install.run_first_load_actions()
85
85
 
86
- utils.db_schema_update()
86
+ try:
87
+ self.version = importlib.metadata.version(conf.app_id)
88
+ print('nowfocus version', self.version)
89
+ except Exception as e:
90
+ print(e)
91
+
92
+ if get_system_db_value('db_schema_version') != self.version:
93
+
94
+ from upgrade import do_upgrades
95
+ do_upgrades(self)
96
+
87
97
 
88
98
  self.async_refresh()
89
99
 
@@ -358,11 +368,8 @@ class Application(Gtk.Application):
358
368
 
359
369
  def resume_session_from_db(self):
360
370
  try:
361
- db_session = db_query("SELECT value FROM system WHERE field = 'session'")
362
-
363
- if db_session:
364
- s = json.loads(db_session[0]['value'])
365
- s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
371
+ s = get_session_from_system_db()
372
+ if s:
366
373
  self.session = s
367
374
  self.is_running = True
368
375
  dbg("resuming session",s['label'],l=2, s='session')
@@ -470,7 +477,7 @@ class Application(Gtk.Application):
470
477
  process.wait()
471
478
  else:
472
479
  print("Launching command with subprocess.run", command)
473
- subprocess.run(command)
480
+ subprocess.run(command,shell=True)
474
481
 
475
482
  print("running task command complete:", command)
476
483
 
@@ -524,14 +531,17 @@ class Application(Gtk.Application):
524
531
 
525
532
  # Get time tracker for this tasks todolist
526
533
  todolist_conf = conf.user['todolists'][task['todolist']]
527
- timetracker_conf = conf.user['timetrackers'][todolist_conf['timetracker']]
528
-
529
- dbg("Save Session to "+ todolist_conf['timetracker'])
530
- try:
531
- save_thread = threading.Thread(target=conf.timetracker_connectors[timetracker_conf['type']].save_session, args=(session,timetracker_conf) )
532
- save_thread.start()
533
- except Exception as e:
534
- error_notice('Error Saving Time Data'," Recording timetracking for "+ task['label']+" in "+timetracker_conf['label']+" had a serious failure",e )
534
+
535
+ if 'timetracker' in todolist_conf:
536
+
537
+ dbg("Save Session to "+ todolist_conf['timetracker'])
538
+ try:
539
+
540
+ timetracker_conf = conf.user['timetrackers'][todolist_conf['timetracker']]
541
+ save_thread = threading.Thread(target=conf.timetracker_connectors[timetracker_conf['type']].save_session, args=(session,timetracker_conf) )
542
+ save_thread.start()
543
+ 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 )
535
545
 
536
546
  session['timetracker'] = todolist_conf['timetracker']
537
547
 
nowfocus/conf.py CHANGED
@@ -71,6 +71,7 @@ prototype_settings = {
71
71
  'max_top_level_menu_items':10,
72
72
  'hours_search_timeframe':'this year',
73
73
  'invoice_hourly_rate':0,
74
+ 'default_list_for_new_tasks':'Most recently used list',
74
75
 
75
76
  "custom_pomodoro_intervals": {
76
77
  "email":7 #minutes
@@ -18,7 +18,7 @@ def add_new_task(user_conf,parent_list,task_label):
18
18
  headers = {'content-type': 'application/json'}
19
19
  data = {'title': task_label}
20
20
 
21
- utils.dbg('add_new_task() parent_list',parent_list, l=-1)
21
+ utils.dbg('add new task to ',parent_list, l=-1)
22
22
 
23
23
  response = requests.put(user_conf['url']+"api/v1/projects/"+str(parent_list['data']['id'])+"/tasks",json=data, headers=head)
24
24
 
@@ -103,11 +103,17 @@ def get_todos(user_conf):
103
103
 
104
104
 
105
105
  headers = {"Authorization": "Bearer "+user_conf['token']}
106
- projects_lists = requests.get(user_conf['url']+"api/v1/projects", headers=headers).json()
107
-
106
+ response = requests.get(user_conf['url']+"api/v1/projects", headers=headers)
107
+ # print("response.headers")
108
+ # print(response.headers)
109
+ projects_lists = response.json()
108
110
  # print("vikunja projects")
109
111
  # print(json.dumps(projects_lists, indent=4))
110
112
 
113
+ if 'message' in projects_lists:
114
+ # This occurs with an expired token
115
+ utils.dbg(projects_lists['message'], notification="Vikunja API Error: "+projects_lists['message'], s='vikunja', l=0)
116
+
111
117
  # for i in projects_list:
112
118
  for i, itemIter in enumerate(projects_lists):
113
119
 
@@ -155,54 +161,6 @@ def get_todos(user_conf):
155
161
  if lists[parent_id]['status'] == -1:
156
162
  lists[id]['status'] = -1
157
163
 
158
-
159
-
160
- # NOTE: Originally tasks where queried per list, however this no longer works well because it requires a 'view' id, which comes wih various un-predictable baked-in filters (like done=true). Using the tasks/all api endpoint avoids this but quickly hits the default maxitemsperpage limit (which could, less obviously, show up in the per list method).
161
-
162
- # else: print('parent_project_id', p['parent_project_id'], 'not in projects_lists')
163
-
164
- # view_id = p['views'][0]['id']
165
- # utils.dbg({'view_id':view_id},'vikunja')
166
-
167
-
168
- # Including 'done' tasks (Doesn't seem o includ them actually)'
169
- # projects_tasks = requests.get(user_conf['url']+"api/v1/projects/"+str(p['id'])+"/views/"+str(view_id)+"/tasks?sort_by[]=position&order_by[]=asc&equalspage=1", headers=headers).json()
170
-
171
- # Filtering out done tasks, Works
172
- # projects_tasks = requests.get(user_conf['url']+"api/v1/projects/"+str(p['id'])+"/views/"+str(view_id)+"/tasks?sort_by[]=position&order_by[]=asc&filter_by[]=done&filter_value[]=false&filter_comparator[]=equals&filter_comparator[]=equalspage=1", headers=headers).json()
173
-
174
- # projects_tasks = requests.get(user_conf['url']+"api/v1/projects/"+str(p['id'])+"/tasks", headers=headers).json()
175
-
176
- # print("vikunja projects_tasks")
177
- # print(json.dumps(projects_tasks, indent=4))
178
- # utils.pretty_print(projects_tasks)
179
-
180
- # for i, itemIter in enumerate(projects_tasks):
181
-
182
- # t = projects_tasks[i]
183
- # t['id'] = str(t['id'])
184
-
185
- # tasks[t['id']] = {
186
- # 'id':t['id'],
187
- # 'label':t['title'],
188
- # 'parent_id':p['id'],
189
- # 'parent_label':p['title'],
190
- # 'todolist':user_conf['id'],
191
- # 'data':t
192
- # }
193
-
194
- # if t['done'] == True:
195
- # tasks[t['id']]['status'] = False
196
-
197
-
198
- # # Show favorites in main menu
199
- # if t['is_favorite'] == True:
200
- # tasks[t['id']]['priority'] = 1
201
- # elif t['priority'] in priorities:
202
- # print("Priority task", t['title'])
203
- # tasks[t['id']]['priority'] = priorities[t['priority']]
204
- # print(tasks[t['id']])
205
-
206
164
  # beware per_page config ( generally in /opt/vikunja/config.yml) maxitemsperpage defaults to 20 so this really won't work on a default setup
207
165
  # maxitemsperpage: 5000
208
166
  # ?per_page=10000
@@ -3,7 +3,7 @@ Name=nowfocus
3
3
  GenericName=Task, Todo, Timer, timertacker
4
4
  Version=1.0
5
5
  Comment=Accomplish one worthwhile thing at a time.
6
- Exec=nowfocus -f
6
+ Exec=nowfocus
7
7
  Icon=nowfocus
8
8
  Terminal=false
9
9
  Type=Application
nowfocus/install.py CHANGED
@@ -80,7 +80,6 @@ def copy_desktop_integration_files():
80
80
  (home+'/.config/autostart/','nowfocus.desktop')
81
81
  )
82
82
 
83
-
84
83
  for file in files:
85
84
  try:
86
85
  Path(file[0]).mkdir(parents=True, exist_ok=True)
@@ -44,10 +44,9 @@ class NewTaskWDialog(Gtk.Dialog):
44
44
  box.add(Gtk.Box(border_width=10)) #spacer
45
45
 
46
46
  default_list = get_most_recent_list(self.app.session)
47
- if default_list:
48
- self.selected_list = db_get_item_by_id(default_list,table='lists')
49
47
 
50
- box.add(choose_from_lists(self.select_list, default_list))
48
+ box.add(choose_from_lists(self.select_list, default_list['id']))
49
+ self.selected_list = default_list
51
50
 
52
51
  box.add(Gtk.Box(border_width=10)) #spacer
53
52
 
@@ -80,7 +79,9 @@ class NewTaskWDialog(Gtk.Dialog):
80
79
  task_label = self.task_label_entry.get_text().strip()
81
80
 
82
81
  if not task_label:
83
- error_notice("Please Enter a name before saving new task")
82
+ self.task_label_entry.grab_focus()
83
+ self.task_label_entry.set_placeholder_text('Please enter a task')
84
+ # error_notice("Please Enter a name before saving new task")
84
85
  return False
85
86
 
86
87
  parent_list = self.selected_list
nowfocus/settings.py CHANGED
@@ -923,7 +923,7 @@ class EditAddConnectorDialog(Gtk.Dialog):
923
923
 
924
924
 
925
925
  if key in ['file']:
926
- self.entries[key] = Gtk.FileChooserButton(title="Select Todo File")
926
+ self.entries[key] = Gtk.FileChooserButton(title="Select "+connector_category+" file")
927
927
  self.entries[key].set_action(Gtk.FileChooserAction.OPEN)
928
928
 
929
929
  else:
nowfocus/task_window.py CHANGED
@@ -399,16 +399,10 @@ class TaskWindow(Gtk.Window):
399
399
  self.r_sidebar.pack_start(self.sessions_scrolledwindow, True, True, 0)
400
400
 
401
401
  self.sessions_box = Gtk.VBox(spacing=5)
402
- # self.sessions_box.get_style_context().add_class("subtle")
403
402
  self.sessions_scrolledwindow.add(self.sessions_box)
404
403
 
405
- # self.r_sidebar.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
406
- # self.r_sidebar.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK)
407
- # self.r_sidebar.connect("enter-notify-event",self.test)
408
- # session = None, accepts_tasks = True, skip_top_level_lists = False, show_todolist_headers = True, deduplicate = False, truncate_labels_to_chars = 100
409
-
410
404
  self.l_sidebar.add(choose_from_lists(
411
- self.select_list_callback, 'None', None, accepts_tasks=False, skip_top_level_lists =True, show_todolist_headers=False, deduplicate=True, truncate_labels_to_chars=20))
405
+ self.select_list_callback, 'None', None, accepts_tasks=False, show_todolist_headers=True, truncate_labels_to_chars=20))
412
406
 
413
407
  self.SessionEditDialog = SessionEditDialog # passed to show_sessions via self
414
408
  sidebar_sessions = get_sessions(use_sessions_timeframe_setting=False)
nowfocus/upgrade.py ADDED
@@ -0,0 +1,99 @@
1
+ import conf
2
+ from utils import *
3
+ # from pkg_resources import parse_version
4
+
5
+
6
+ def do_upgrades(app):
7
+ db_schema_version = get_system_db_value('db_schema_version')
8
+
9
+ if db_schema_version == '0.2':
10
+
11
+ db_query("ALTER TABLE lists DROP COLUMN status")
12
+ db_query("ALTER TABLE tasks DROP COLUMN status")
13
+
14
+ db_query("ALTER TABLE lists ADD COLUMN status INTEGER DEFAULT 1")
15
+ db_query("ALTER TABLE tasks ADD COLUMN status INTEGER DEFAULT 1")
16
+
17
+ db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.3')")
18
+
19
+ db_schema_version = '0.3'
20
+
21
+
22
+ if db_schema_version == '0.3':
23
+ db_query("ALTER TABLE sessions ADD COLUMN timetracker TEXT")
24
+ db_query("ALTER TABLE sessions ADD COLUMN notes TEXT")
25
+ db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.4')")
26
+ print('adding timetracker column to session table')
27
+
28
+ for todolist_id, todo in conf.user['todolists'].items():
29
+ db_query("UPDATE sessions SET timetracker = ? WHERE todolist = ?",(todo['timetracker'],todolist_id) )
30
+
31
+ db_schema_version = '0.4'
32
+
33
+ if db_schema_version == '0.4':
34
+ dbg('Schema Update from', db_schema_version, 'to 0.5',l=-1)
35
+
36
+ print('Adding priority column to session table')
37
+
38
+ db_query("ALTER TABLE sessions ADD COLUMN priority INTEGER DEFAULT 0")
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
+
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
+ db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.5')")
49
+
50
+ db_schema_version = '0.5'
51
+
52
+ if db_schema_version == '0.5':
53
+ dbg('Schema Update from', db_schema_version, 'to 0.5.6',l=-1)
54
+
55
+ from install import copy_desktop_integration_files
56
+ copy_desktop_integration_files()
57
+
58
+ db_schema_version = "0.5.6"
59
+ set_system_db_value("db_schema_version",db_schema_version)
60
+
61
+ # if db_schema_version == 0.5:
62
+ # db_query("ALTER TABLE tasks ADD COLUMN tags TEXT DEFAULT '{}'")
63
+ # db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.5')")
64
+ # db_schema_version = 0.6
65
+
66
+
67
+ dbg('db_schema_version updated to', db_schema_version,s='db')
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ # update time_target format
82
+ # for id, tt in user['time_targets']['lists'].items():
83
+ # if 'within_value' not in tt:
84
+ # print("Updating time target to new format ",tt)
85
+ # tt['within_value'] = tt['num_days']
86
+ # tt['within_unit'] = 'days'
87
+ # print(tt)
88
+ # if 'status' not in tt:
89
+ # tt['status'] = True
90
+
91
+
92
+ # for id, tt in user['time_targets']['tasks'].items():
93
+ # if 'within_value' not in tt:
94
+ # print("Updating time target to new format ",tt)
95
+ # tt['within_value'] = tt['num_days']
96
+ # tt['within_unit'] = 'days'
97
+ # print(tt)
98
+ # if 'status' not in tt:
99
+ # tt['status'] = True
nowfocus/utils.py CHANGED
@@ -7,7 +7,6 @@ from datetime import datetime, timezone, timedelta
7
7
  from dateutil.relativedelta import relativedelta
8
8
  import copy
9
9
  import threading
10
-
11
10
  import sqlite3
12
11
  from contextlib import closing
13
12
  from pathlib import Path
@@ -30,11 +29,11 @@ notify.init(conf.app_name)
30
29
  lists = {}
31
30
 
32
31
 
33
- def dbg(*data, s="", l=2, e=None, notify=None):
32
+ def dbg(*data, s="", l=2, e=None, notification=None):
34
33
  ''' Any number of positional args optionally
35
34
  l: level {-1: Default output, 0:Error, 1:Warning, 2:Info, 3:Details}
36
35
  s: system (Debuggable systems: 'taskwindow','signals','todoloading','user_settings', 'targets', 'performance')
37
- notify: Show a notification with the content of notify.
36
+ notification: Show a notification with the content of notification.
38
37
  e (Exception object) traceback will be printed
39
38
 
40
39
  '''
@@ -47,8 +46,8 @@ def dbg(*data, s="", l=2, e=None, notify=None):
47
46
 
48
47
  if "all" in conf.debug_systems or system in conf.debug_systems or level <= conf.debug_level:
49
48
 
50
- if notify:
51
- notify.Notification.new(conf.app_name+" "+levels[level], str(notify), None).show()
49
+ if notification:
50
+ notify.Notification.new(conf.app_name+" "+levels[level], str(notification), None).show()
52
51
 
53
52
  filter(None,data)
54
53
  if len(data) == 1:
@@ -68,7 +67,7 @@ def dbg(*data, s="", l=2, e=None, notify=None):
68
67
 
69
68
  if e and isinstance(e,Exception):
70
69
  traceback.print_tb(e.__traceback__)
71
- print(e)
70
+ print(e,"\n")
72
71
 
73
72
  def error_notice(title, details = None, e = None):
74
73
  print('ERROR',title,details,e)
@@ -268,20 +267,20 @@ def extended_label(i):
268
267
  return o
269
268
 
270
269
  # TODO cleanup this
271
- def lists_cache(new_lists = None):
272
- '''Set or get a dict of (all) lists. This is useful but very gimpy. If new_lists is supplied it will exclusively use that (until it is replaced or the global lists var is emptied)'''
270
+ def lists_cache(new_lists = None, reset = False):
271
+ '''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
+
273
273
  global lists
274
274
 
275
275
  if(new_lists):
276
276
  lists = new_lists
277
- # TODO: add this to the existing rather than overwriting...
277
+ # TODO: add a param to append to the existing lists rather than overwriting...
278
278
 
279
- elif(lists == {}):
280
- lists = db_query('SELECT * FROM lists WHERE status = 1 ORDER BY extended_label DESC',None, 'id') # TODO: limit to active todolists
279
+ elif reset or lists == {}:
280
+ lists = db_query('SELECT * FROM lists WHERE status = 1 ORDER BY extended_label DESC',None, 'id')
281
281
 
282
- # print('list_cache l',lists)
283
- for l in lists.items():
284
- lists[l['id']]['data'] = json.loads(l['data'])
282
+ for id, l in lists.items():
283
+ lists[l['id']] = proc_db_item(l,'lists')
285
284
 
286
285
  return lists
287
286
 
@@ -336,7 +335,7 @@ def db_query(sql,parameters=None,key=None,error_handling=1):
336
335
  return result
337
336
 
338
337
  except Exception as e:
339
- dbg(e,"sql",sql,'parameters',parameters,s="db",l=0)
338
+ dbg("sql",sql,'parameters',parameters,s="db",l=0,e=e)
340
339
  if error_handling > 0:
341
340
  error_notice("database error",str(e))
342
341
  if error_handling > 1:
@@ -347,88 +346,6 @@ def db_query(sql,parameters=None,key=None,error_handling=1):
347
346
  return []
348
347
 
349
348
 
350
- def db_schema_update():
351
-
352
- try:
353
- db_schema_version = float(db_query("SELECT field, value FROM system WHERE field = 'db_schema_version' ",error_handling=2)[0]['value'])
354
- except Exception as e:
355
- print("Updating db_schema_version to 0.2")
356
-
357
- db_query("CREATE TABLE system (field TEXT PRIMARY KEY NOT NULL, value TEXT)")
358
- db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.2')")
359
-
360
- db_schema_version = float(db_query("SELECT field, value FROM system WHERE field = 'db_schema_version' ")[0]['value'])
361
-
362
- db_query("ALTER TABLE lists ADD COLUMN status INTEGER DEFAULT 1")
363
- db_query("ALTER TABLE tasks ADD COLUMN status INTEGER DEFAULT 1")
364
-
365
- db_query("ALTER TABLE lists ADD COLUMN extended_label TEXT")
366
- db_query("ALTER TABLE tasks ADD COLUMN extended_label TEXT")
367
-
368
- db_query("ALTER TABLE sessions ADD COLUMN extended_label TEXT")
369
-
370
- # Since these column are just a cache, replace instead of copying columns
371
- db_query("ALTER TABLE tasks DROP COLUMN priority")
372
- db_query("ALTER TABLE tasks DROP COLUMN data")
373
- db_query("ALTER TABLE lists DROP COLUMN priority")
374
- db_query("ALTER TABLE lists DROP COLUMN data")
375
-
376
- db_query("ALTER TABLE lists ADD COLUMN priority INTEGER DEFAULT 0")
377
- db_query("ALTER TABLE lists ADD COLUMN data TEXT DEFAULT '{}'")
378
- db_query("ALTER TABLE tasks ADD COLUMN priority INTEGER DEFAULT 0")
379
- db_query("ALTER TABLE tasks ADD COLUMN data TEXT DEFAULT '{}'")
380
-
381
-
382
- if db_schema_version == 0.2:
383
-
384
- db_query("ALTER TABLE lists DROP COLUMN status")
385
- db_query("ALTER TABLE tasks DROP COLUMN status")
386
-
387
- db_query("ALTER TABLE lists ADD COLUMN status INTEGER DEFAULT 1")
388
- db_query("ALTER TABLE tasks ADD COLUMN status INTEGER DEFAULT 1")
389
-
390
- db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.3')")
391
-
392
- db_schema_version = 0.3
393
-
394
-
395
- if db_schema_version == 0.3:
396
- db_query("ALTER TABLE sessions ADD COLUMN timetracker TEXT")
397
- db_query("ALTER TABLE sessions ADD COLUMN notes TEXT")
398
- db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.4')")
399
- print('adding timetracker column to session table')
400
-
401
- for todolist_id, todo in conf.user['todolists'].items():
402
- db_query("UPDATE sessions SET timetracker = ? WHERE todolist = ?",(todo['timetracker'],todolist_id) )
403
-
404
- db_schema_version = 0.4
405
-
406
- if db_schema_version == 0.4:
407
- print('Adding priority column to session table')
408
-
409
- db_query("ALTER TABLE sessions ADD COLUMN priority INTEGER DEFAULT 0")
410
-
411
- for session in db_query("SELECT DISTINCT task_id, extended_label FROM sessions"):
412
- t = db_get_item_by_id(session['task_id'],dgb_error_level_for_failure=3)
413
- if t and t['priority'] > 0:
414
- print("setting",session['extended_label']," session priority to ",t['priority'] )
415
-
416
- db_query("UPDATE sessions SET priority = ? WHERE task_id = ?",(t['priority'],session['task_id']))
417
-
418
- print("Total hours priority sessions",round(divide(db_query("SELECT SUM(duration) as total FROM sessions WHERE priority > 0")[0]['total'],3600),2))
419
- db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.5')")
420
-
421
- db_schema_version = 0.5
422
-
423
- # if db_schema_version == 0.5:
424
- # db_query("ALTER TABLE tasks ADD COLUMN tags TEXT DEFAULT '{}'")
425
- # db_query("REPLACE INTO system(field, value) VALUES('db_schema_version', '0.5')")
426
- # db_schema_version = 0.6
427
-
428
-
429
- dbg('db_schema_version', db_schema_version,s='db')
430
-
431
-
432
349
  def reindex(t=None):
433
350
  if t:
434
351
  thread = threading.Thread(target=reindex_one,args=(t,))
@@ -447,7 +364,9 @@ def reindex_all():
447
364
  for tid, t in get_timetarget_priority_tasks().items():
448
365
  db_query("UPDATE taskindex set priority = ? WHERE id = ?",(t['priority'],t['id']))
449
366
 
450
- db_query("REPLACE INTO system(field, value) VALUES(?,?)",('taskindex_update_time',now().strftime("%Y-%m-%d %H:%M:%S")))
367
+
368
+ 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")))
451
370
  # print('reindex done')
452
371
  timeit()
453
372
 
@@ -464,14 +383,18 @@ def reindex_one(t):
464
383
 
465
384
  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'],))
466
385
 
467
- print("reindexed",t['label'])
386
+ dbg("reindexed",t['label'])
468
387
  # print(db_query("SELECT * FROM taskindex where id = ?",(t['id'],)))
469
- db_query("REPLACE INTO system(field, value) VALUES(?,?)",('taskindex_update_time',now().strftime("%Y-%m-%d %H:%M:%S")))
388
+ # db_query("REPLACE INTO system(field, value) VALUES(?,?)",('taskindex_update_time',now().strftime("%Y-%m-%d %H:%M:%S")))
389
+ set_system_db_value('taskindex_update_time', now().strftime("%Y-%m-%d %H:%M:%S"))
390
+
470
391
  return True
471
392
 
472
393
 
473
394
  def taskindex_updated_time():
474
395
  ''' returns system db taskindex_update_time string formatted as %Y-%m-%d %H:%M:%S '''
396
+ get_system_db_value('taskindex_update_time')
397
+
475
398
  update_time = db_query("SELECT value FROM system WHERE field = 'taskindex_update_time'")[0]['value']
476
399
 
477
400
  # print('taskindex_updated_time',update_time)
@@ -631,7 +554,7 @@ def db_set_todolist(todolist_id,lists,tasks):
631
554
  insert_tasks[id] = db_prepare_item(i,'tasks')
632
555
 
633
556
  # Clear incomplete lists cache
634
- lists_cache({})
557
+ lists_cache(reset=True)
635
558
 
636
559
  # for testing
637
560
  # insert_tasks = {'3':insert_tasks['3'],'294':insert_tasks['294']}
@@ -709,13 +632,17 @@ def db_cleanup(widget = None):
709
632
 
710
633
  # exit()
711
634
 
712
- def db_get_item_by_id(id,table = 'tasks',dgb_error_level_for_failure=0):
635
+ def db_get_item_by_id(id, table = 'tasks',dgb_error_level_for_failure=0):
713
636
 
714
637
  if table in ['task','list']:
715
638
  table = table+'s'
716
639
  if table not in ['tasks','lists']:
717
640
  dbg('bad table "'+str(table)+'" passed to db_get_item_by_id',id,l=0)
718
641
  return {}
642
+
643
+ # in case an item dict was passed instead of an id
644
+ if isinstance(id, dict):
645
+ return id
719
646
 
720
647
  try:
721
648
  data = db_query("SELECT * FROM "+table+" WHERE id = ?",(id,))
@@ -865,7 +792,8 @@ def db_set_session_cache(s):
865
792
  ''' Add active session to system db table. Not to be confused with db_save_session '''
866
793
  db_session = copy.deepcopy(s)
867
794
  db_session['start_time'] = db_session['start_time'].strftime("%Y-%m-%d %H:%M:%S.%f%z")
868
- db_query("REPLACE INTO system(field, value) VALUES(:field, :value)", {'field':'session','value':json.dumps(db_session)})
795
+
796
+ set_system_db_value('session', json.dumps(db_session))
869
797
 
870
798
 
871
799
  def get_total_time(id, category = 'tasks', start_time = None, end_time = None, get_minutes = None):
@@ -1057,17 +985,17 @@ def refresh_todolist(todo, catch_errors = False):
1057
985
  ''' Refresh a single todo. runs db_set_todolist and returns todos. Exceptions must be handled my caller '''
1058
986
 
1059
987
  try:
1060
- dbg('Refreshing '+todo['label'],l=-1)
988
+ dbg('Refreshing '+todo['label'],l=2)
1061
989
 
1062
990
  todos = conf.todo_connectors[todo['type']].get_todos(todo)
1063
991
 
1064
- dbg('Done Refreshing '+todo['label'],l=-1)
992
+ dbg('Refreshing '+todo['label'],l=-1)
1065
993
 
1066
994
  db_set_todolist(todo['id'],todos['lists'],todos['tasks'])
1067
995
 
1068
996
  except Exception as e:
1069
997
  if catch_errors:
1070
- error_notice('Error Loading '+todo['label']+' i refresh_todolist',e)
998
+ dbb(notify='Error Loading '+todo['label']+' i refresh_todolist',e=e)
1071
999
  todos = {'lists': {}, 'tasks':{}}
1072
1000
 
1073
1001
  else:
@@ -1088,13 +1016,13 @@ def handle_todo_read_error(todo_conf,e):
1088
1016
  if isinstance(e, (FileExistsError, FileNotFoundError, PermissionError)):
1089
1017
  handle_todo_file_access_error(todo_conf,e)
1090
1018
 
1091
- dbg(todo_conf['label']+ " Access Error",e,l=0,notify=True)
1019
+ dbg(todo_conf['label']+ " Access Error",e,l=0,notification=True)
1092
1020
 
1093
1021
 
1094
1022
  def handle_todo_file_read_error(todo_conf,e):
1095
1023
  conf.user['todolists'][todo_conf['id']]['status'] = False
1096
1024
  save_user_settings()
1097
- dbg(todo_conf['label']+ " Deactivated",e,l=-1,notify=True)
1025
+ dbg(todo_conf['label']+ " Deactivated",e,l=-1,notification=True)
1098
1026
 
1099
1027
 
1100
1028
  def get_todolists(use_db_cache = False):
@@ -1125,12 +1053,12 @@ def get_todolists(use_db_cache = False):
1125
1053
  if conf.debug_level == 3 or 'todoloading' in conf.debug_systems or todo['type'] in conf.debug_systems :
1126
1054
  raise e
1127
1055
 
1128
- error_notice('Error refreshing '+todo['label'],e)
1056
+ dbg(notification='Error refreshing '+todo['label'],e=e)
1129
1057
 
1130
1058
  try:
1131
1059
  todos = db_get_todolist(todo['id'])
1132
1060
  except Exception as e:
1133
- error_notice('Also Failed to load '+todo['label']+' From cache' ,e)
1061
+ error_notice('Also Failed to load '+todo['label']+' From cache' ,e=e)
1134
1062
  todos = {'lists': {}, 'tasks':{}}
1135
1063
 
1136
1064
  tasks.update(todos['tasks'])
@@ -1147,17 +1075,79 @@ def get_todolists(use_db_cache = False):
1147
1075
  return o
1148
1076
 
1149
1077
 
1078
+ def get_system_db_value(field, json = False):
1079
+ try:
1080
+ o = db_query("SELECT value FROM system WHERE field = ? ",(field,))
1081
+ if o:
1082
+ o = o[0]['value']
1083
+ else:
1084
+ dbg('no system_db_value for', field, l=1)
1085
+
1086
+ if json:
1087
+ o = json.loads(o)
1088
+ return o
1089
+ except Exception as e:
1090
+ dbg("Error getting",field, 'from system db',e,l=0)
1091
+
1092
+
1093
+ def set_system_db_value(field, value, json = False):
1094
+ try:
1095
+ if json:
1096
+ value = json.dumps(value)
1097
+
1098
+ db_query("REPLACE INTO system(field, value) VALUES(:field, :value)", {'field':field,'value':value})
1099
+
1100
+ except Exception as e:
1101
+ dbg("Error setting", field, value, 'in system db',e,l=0)
1102
+
1103
+
1104
+ def get_session_from_system_db():
1105
+ try:
1106
+ db_session = db_query("SELECT value FROM system WHERE field = 'session'")
1107
+
1108
+ if db_session:
1109
+ s = json.loads(db_session[0]['value'])
1110
+ s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
1111
+ return s
1112
+ except Exception as e:
1113
+ dbg("Error in get_session_from_system_db session",e,l=1)
1114
+
1115
+
1150
1116
  def get_most_recent_list(session = None):
1117
+ ''' returns a list object '''
1118
+ lists = lists_cache()
1119
+ # print(lists.keys())
1120
+
1121
+ try:
1122
+ return lists[session['task']['parent_id']]
1123
+
1124
+ except Exception as e:
1125
+ pass
1126
+
1127
+ try:
1128
+ return lists[get_session_from_system_db()['task']['parent_id']]
1129
+
1130
+ except Exception as e:
1131
+ pass
1132
+
1133
+ try:
1134
+ return lists[db_query("SELECT parent_id FROM sessions WHERE parent_id IN (SELECT id FROM lists WHERE status = 1) ORDER BY sessions.start_time DESC LIMIT 1")[0]['parent_id']]
1135
+
1136
+ except Exception as e:
1137
+ return lists.items()[0]
1151
1138
 
1152
- if session and 'task' in session and 'parent_id' in session['task']:
1153
- return session['task']['parent_id']
1154
1139
 
1155
- last_session = db_query("SELECT parent_id FROM sessions WHERE parent_id IN (SELECT id FROM lists WHERE status = 1) ORDER BY sessions.start_time DESC LIMIT 1")
1156
- if last_session:
1157
- return last_session[0]['parent_id']
1158
1140
 
1141
+ def get_default_list_for_new_tasks():
1142
+ # if conf.user['default_list_for_new_tasks'] != 'Most recently used list':
1143
+ # try:
1159
1144
 
1160
- def choose_from_lists(callback, selected_list_id = None, session = None, accepts_tasks = True, skip_top_level_lists = False, show_todolist_headers = True, deduplicate = False, truncate_labels_to_chars = 100):
1145
+ # List that was last added to
1146
+
1147
+ return get_most_recent_list()
1148
+
1149
+
1150
+ def choose_from_lists(callback, selected_list_id = None, session = None, accepts_tasks = True, show_todolist_headers = True, truncate_labels_to_chars = 100):
1161
1151
  ''' Returns a Gtk.ScrolledWindow widget with radio buttons'''
1162
1152
 
1163
1153
  # TODO: consider using TreeView https://lazka.github.io/pgi-docs/Gtk-3.0/classes/TreeView.html#Gtk.TreeView
@@ -1175,28 +1165,20 @@ def choose_from_lists(callback, selected_list_id = None, session = None, accepts
1175
1165
  # scrolled_window.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
1176
1166
  scrolled_window.add(box)
1177
1167
 
1178
- todolist = None
1179
-
1180
- conditions = " "
1181
-
1182
- if skip_top_level_lists:
1183
- conditions += " AND parent_label IS NOT NULL "
1184
-
1185
- if deduplicate:
1186
- conditions += " GROUP BY extended_label "
1187
-
1188
- lists = db_query("SELECT lists.*, (SELECT start_time FROM sessions WHERE todolist = lists.todolist ORDER BY start_time DESC LIMIT 1 ) as last_session_on_todo FROM lists WHERE status = '1' "+conditions+" ORDER BY last_session_on_todo DESC, CASE WHEN parent_id IS NULL THEN 1 ELSE 2 END, extended_label ASC ", None, 'id')
1168
+ lists = db_query("SELECT lists.*, (SELECT start_time FROM sessions WHERE todolist = lists.todolist ORDER BY start_time DESC LIMIT 1 ) as last_session_on_todo FROM lists WHERE status = '1' ORDER BY last_session_on_todo DESC, CASE WHEN parent_id IS NULL THEN 1 ELSE 2 END, extended_label ASC ", None, 'id')
1189
1169
 
1190
1170
  r_button = None
1191
1171
  todolist_id = None
1192
1172
 
1193
1173
  for id, l in lists.items():
1194
1174
  try:
1195
- l['data'] = json.loads(l['data'])
1175
+ l = proc_db_item(l)
1196
1176
  if len(l['label']) > truncate_labels_to_chars:
1197
1177
  l['label'] = l['label'][:truncate_labels_to_chars] + '…'
1178
+
1179
+ nested_list_count = l['extended_label'].count(' > ')
1198
1180
 
1199
- label = f'{" · " * l['extended_label'].count(' > ')}' + GLib.markup_escape_text(l['label'])
1181
+ label = f'{" · " * nested_list_count}' + GLib.markup_escape_text(l['label'])
1200
1182
 
1201
1183
  if todolist_id != l['todolist']:
1202
1184
  todolist_id = l['todolist']
@@ -1213,14 +1195,12 @@ def choose_from_lists(callback, selected_list_id = None, session = None, accepts
1213
1195
 
1214
1196
  if accepts_tasks and l['data']['accepts_tasks'] == False:
1215
1197
  label_widget.set_markup(' '+label)
1216
-
1217
1198
  box.add(label_widget)
1218
1199
 
1219
1200
  else:
1220
1201
  r_button = Gtk.RadioButton(group=r_button)
1221
1202
  r_button.add(label_widget)
1222
1203
 
1223
-
1224
1204
  if l['id'] == selected_list_id:
1225
1205
  r_button.set_active(True)
1226
1206
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nowfocus
3
- Version: 0.5.5
3
+ Version: 0.5.7
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
@@ -58,7 +58,7 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
58
58
  - Task prioritization
59
59
  - Infinitely nestable lists
60
60
  - Pomodoro timer
61
- - Time targets: set a minimum or maximum time for any task or list of tasks and get reminded to follow though
61
+ - Time targets: set a minimum or maximum time for any task or list of tasks and get reminded to follow-through
62
62
  - Randomness interrupt bell (optional) to keep you on track with tracking your time
63
63
  - Keyboard-driven interface
64
64
  - Offline to-do list cache
@@ -1,18 +1,18 @@
1
1
  nowfocus/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- nowfocus/__main__.py,sha256=ow_EHA8LCeC6OG0PW-cMdHQuU6dcGedOyPec68mIb2U,33169
3
- nowfocus/conf.py,sha256=KV0iouYSSpYUITLlG-lPvmk1sHFVQsykwqmvb9wyTdk,6693
2
+ nowfocus/__main__.py,sha256=1WffOUaxlfw0gc_qWY9K78j35AmoxoDDLePoK3wFlE0,33433
3
+ nowfocus/conf.py,sha256=czmeMU7r5DD5fT1cDi2kjwYa5xQSXJoQoeQYsoFMkzc,6757
4
4
  nowfocus/example-todo.txt,sha256=o-ZRNiTlSGFbTK9jpdDIi07qHBrpvP0JhBZSk0VlpQU,246
5
- nowfocus/install.py,sha256=tcds4l6b492HTC7DfapatafgeMmDWvBfy8nwi1CjFXk,3040
6
- nowfocus/new_task_dialog.py,sha256=GG49tOAwXiUKAHeaKnCG8Q3SZlL5pJijRZEcSjMwQng,4350
5
+ nowfocus/install.py,sha256=gzjDzqUFYRP1dS_zA_MKrTZWTAB1bYqcxjRw5mBn1aQ,3039
6
+ nowfocus/new_task_dialog.py,sha256=LwvwNlCg_rl0tHwLPro0Jx1p4J_ukMNM2nLQ36vUJPI,4429
7
7
  nowfocus/session_edit_dialog.py,sha256=V2QWSdNaxsQHRcG28CJBQM2sa45m5RNcu_suQF26mkM,6912
8
8
  nowfocus/session_options.py,sha256=mOnyEM3-usKgVvBhESY0TNBcy4FTG-AHP6ETMgB1fQ4,5071
9
9
  nowfocus/sessions.csv,sha256=kYpr06yQg_J86NQ4AiYw4RnQchcw3ouPKVYa1lYDUNo,39
10
- nowfocus/settings.py,sha256=CZoSpFacxAs99Nq-NXNDKiBqjPSLPwg8bL5hcsi2a6E,35151
10
+ nowfocus/settings.py,sha256=0d6nZw1t36TjyUr9KQ_wDwuh2SB64gUkZeHEjEV867Y,35169
11
11
  nowfocus/styles.css,sha256=PG1SrLkwSSay8M2VKeRcE0UdK54ndsEDFnRLRkmP-9M,510
12
- nowfocus/task_window.py,sha256=UIrHljw3IfnBjdK9idnzgB4cT6QfYY7PChCQhUR_0zI,29373
12
+ nowfocus/task_window.py,sha256=f6eaxrFwI5POJG2tZX0H3AKFA6dGt_RH42y2ydHqosY,28875
13
+ nowfocus/upgrade.py,sha256=S0nyWmVLnukWY0QhC5873HFQZpcWuZ_XEPU4QTBtX3s,3379
13
14
  nowfocus/user_idle_time.py,sha256=I44Ip-iGGzWAiHnM2_06jNqhCne9y1SbvcBI-nYBolU,2365
14
- nowfocus/utils.py,sha256=OyLUTfUqODkMm_QtnQc65ihgCO-cbcMjAWbCKC2uePs,51147
15
- nowfocus/version_migrator.py,sha256=q8T1C8-DLOwUQUM5IPcMjPbVbsLTO4VsqADlAAXd9gw,628
15
+ nowfocus/utils.py,sha256=tVa4rJf0QKhDU5as4-BSIFvuZLMsxGScJkxGRYibtzA,49187
16
16
  nowfocus/connectors/activitywatch.py,sha256=QbkOmjIOiVwccWc2xhhePd0Abww5vEiVpCNjeqOyYGg,921
17
17
  nowfocus/connectors/caldav.py,sha256=PeM_9yJC8W17L8Y5AyS75o6GfzTrPoMYKIvetND8T78,5089
18
18
  nowfocus/connectors/csv.py,sha256=FwMpHM5lPIT90HKBCQUncpaW7zqFjlHjMwKR0-XWg-4,821
@@ -23,8 +23,8 @@ nowfocus/connectors/todo_template.py,sha256=R37fA2LXo8_LpWIgqozytI5RqIUjGggFHup2
23
23
  nowfocus/connectors/todotxt.py,sha256=QCZjbIhY4Lm37YD0GsKJQUqbj7s3eYmZGgRwUCndZ5w,3857
24
24
  nowfocus/connectors/trello.py,sha256=VqwnvHGXXcljmdf6kRZcE6sfeBQYhped_KVBEBOzWXM,6072
25
25
  nowfocus/connectors/txt.py,sha256=iskJsw3dZnI4bIeEDtZCY-aQfKRKtoGATEJ0k13npxI,8125
26
- nowfocus/connectors/vikunja.py,sha256=Lg1lgF3C5B606CUf3-U8pVtPt7Cp6IKkT5wFN779V8w,10799
27
- nowfocus/desktop-extras/nowfocus.desktop,sha256=PyCDMgm-aicRJkG2p1czLjBBp3BJz8vfLG7TjgJjZfY,297
26
+ nowfocus/connectors/vikunja.py,sha256=sutd-loOjYT0njtEIT-E_E_EG9KTdsJfsYd1mHeqNBE,8807
27
+ nowfocus/desktop-extras/nowfocus.desktop,sha256=0kWsx0ZfvPbubGG1uuFSHxxYUw2GV9Ly_rtlboM1mak,294
28
28
  nowfocus/desktop-extras/nowfocus.png,sha256=P5rn6-0EAJa2WXf4SJoaNtLRUfiV3LdsOroPKsR6GfA,15148
29
29
  nowfocus/desktop-extras/nowfocus.svg,sha256=nps7naZzuhWWuKzQbpvxr9wLyzjmzMPzNHSBQMVetOo,2137
30
30
  nowfocus/icon/cancel.png,sha256=Hy9A7KdO13MsXguIgoTI276mECN07rzd6hGbIqxSl7c,506
@@ -52,9 +52,9 @@ nowfocus/icon/settings.svg,sha256=fgkGJouPPtZLxZn2nr_5pEp9MdhRSRaW9mtdxhJHDuQ,39
52
52
  nowfocus/sound/bell-xylophone-g.mp3,sha256=1OBcRWvD87AGNcq1uZFR8HqG0nanJykImERfVDVxHD4,53891
53
53
  nowfocus/sound/dinner-bell.mp3,sha256=hjjO0xqA4uXpYw9KLwwlBnrVfRhVq1K5OXzwlMXhRn4,113620
54
54
  nowfocus/sound/xylophone-chord.mp3,sha256=gwgBSqhMt5PMzT5N03Z6TvDgipQZfnkEz_o81Rq5Z1U,131806
55
- nowfocus-0.5.5.dist-info/licenses/LICENSE,sha256=fSJzoHs1EOCwEd7FIyokFeGEma7NKmTVEdHkCr5OIV4,35127
56
- nowfocus-0.5.5.dist-info/METADATA,sha256=aGBt4lGMum7iyPyQGR283NutEy81KuhT3dpAYt51HuA,6804
57
- nowfocus-0.5.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
58
- nowfocus-0.5.5.dist-info/entry_points.txt,sha256=RbYY19-irSoNVglNeNnL9D36cHft7aKsaEGEYoSH3pA,51
59
- nowfocus-0.5.5.dist-info/top_level.txt,sha256=3uLd9BwmfarZwqVUxkSJuVwJ8qHzjThte8rt_UYG7tE,9
60
- nowfocus-0.5.5.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,20 +0,0 @@
1
-
2
- # update time_target format
3
- for id, tt in user['time_targets']['lists'].items():
4
- if 'within_value' not in tt:
5
- print("Updating time target to new format ",tt)
6
- tt['within_value'] = tt['num_days']
7
- tt['within_unit'] = 'days'
8
- print(tt)
9
- if 'status' not in tt:
10
- tt['status'] = True
11
-
12
-
13
- for id, tt in user['time_targets']['tasks'].items():
14
- if 'within_value' not in tt:
15
- print("Updating time target to new format ",tt)
16
- tt['within_value'] = tt['num_days']
17
- tt['within_unit'] = 'days'
18
- print(tt)
19
- if 'status' not in tt:
20
- tt['status'] = True