nowfocus 0.4.0__py3-none-any.whl → 0.4.4__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.
@@ -9,8 +9,6 @@ import requests
9
9
  import conf
10
10
  import utils
11
11
 
12
- # UPDATE sessions SET task_id = "Vikunja_t" || task_id WHERE todolist = 'Vikunja';
13
- # UPDATE sessions SET parent_id = "Vikunja_l" || parent_id WHERE todolist = 'Vikunja';
14
12
 
15
13
  def vikunja_item_id(id, item_type, user_conf):
16
14
  return user_conf['id'] +'_'+ item_type[0] + str(id)
@@ -20,7 +18,7 @@ def add_new_task(user_conf,parent_list,task_label):
20
18
  headers = {'content-type': 'application/json'}
21
19
  data = {'title': task_label}
22
20
 
23
- utils.dbg('add_new_task() parent_list',parent_list, l=0)
21
+ utils.dbg('add_new_task() parent_list',parent_list, l=-1)
24
22
 
25
23
  response = requests.put(user_conf['url']+"api/v1/projects/"+str(parent_list['data']['id'])+"/tasks",json=data, headers=head)
26
24
 
@@ -104,7 +102,6 @@ def get_todos(user_conf):
104
102
  }
105
103
 
106
104
 
107
- priorities = {'DO NOW':1,'Urgent':2,'high':3}
108
105
  headers = {"Authorization": "Bearer "+user_conf['token']}
109
106
  projects_lists = requests.get(user_conf['url']+"api/v1/projects", headers=headers).json()
110
107
 
@@ -139,6 +136,9 @@ def get_todos(user_conf):
139
136
 
140
137
  lists[id]['data']['accepts_tasks'] = True
141
138
 
139
+ # print(p['title']+' is_archived:',p['is_archived'])
140
+ # BUG: archived list aren't provided (But used to be ?) so sublists end up orphaned and dangerous...
141
+
142
142
  if p['is_archived']:
143
143
  lists[id]['status'] = -1
144
144
  else:
@@ -152,6 +152,10 @@ def get_todos(user_conf):
152
152
  if parent_id in lists:
153
153
  lists[id]['parent_label'] = lists[parent_id]['label']
154
154
 
155
+ if lists[parent_id]['status'] == -1:
156
+ lists[id]['status'] = -1
157
+
158
+
155
159
 
156
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).
157
161
 
@@ -204,7 +208,6 @@ def get_todos(user_conf):
204
208
  # ?per_page=10000
205
209
 
206
210
 
207
-
208
211
  response = requests.get(user_conf['url']+"api/v1/tasks/all", headers=headers)
209
212
  all_tasks = response.json()
210
213
 
@@ -218,8 +221,6 @@ def get_todos(user_conf):
218
221
  # print(json.dumps(all_tasks, indent=4))
219
222
  utils.dbg("Got ",len(all_tasks),"Vikunja tasks", s='vikunja', l=2 )
220
223
 
221
- utils.dbg("Got ",len(all_tasks),"Vikunja tasks", s='vikunja', l=2 )
222
-
223
224
  for i, itemIter in enumerate(all_tasks):
224
225
 
225
226
  t = all_tasks[i]
@@ -228,7 +229,7 @@ def get_todos(user_conf):
228
229
 
229
230
  # utils.dbg(t, s='vikunja', l=3)
230
231
 
231
- # Limit to asks assigned, created by, or favorated by user
232
+ # Limit to tasks assigned, created by, or favorated by user
232
233
  if user_conf["username"]:
233
234
  if t['is_favorite'] != True:
234
235
 
@@ -264,13 +265,12 @@ def get_todos(user_conf):
264
265
  tasks[id]['status'] = -1
265
266
 
266
267
  except Exception as e:
267
- print("No list found for", t['title']," list_id",list_id)
268
+ utils.dbg("No list found for", t['title']," list_id",list_id, s='vikunja')
268
269
  # raise e
269
270
 
270
271
  if t['done'] == True:
271
272
  tasks[id]['status'] = 0
272
273
 
273
- # Prioritize if "due_date" is soon or passed (and not: "0001-01-01T00:00:00Z")
274
274
  if 'due_date' in t and t['due_date'] != "0001-01-01T00:00:00Z":
275
275
 
276
276
  due_date = datetime.strptime(t['due_date'],'%Y-%m-%dT%H:%M:%SZ')
@@ -282,14 +282,13 @@ def get_todos(user_conf):
282
282
  if due_seconds > -86400:
283
283
  tasks[id]['priority'] = 2
284
284
 
285
- # prioritize favorites
286
- if t['is_favorite'] == True:
285
+ if t['priority'] and t['priority'] > 1:
286
+ # Vikunja priority is 1 = lowest, 5 = highest
287
+ #
288
+ tasks[id]['priority'] = utils.invert_number_scale(utils.clamp(utils.force_number(t['priority']),0,5))
289
+ elif t['is_favorite'] == True:
287
290
  tasks[id]['priority'] = 2
288
- elif t['priority'] in priorities:
289
- print("Priority task", t['title'])
290
- tasks[id]['priority'] = priorities[t['priority']]
291
- # print(tasks[id])
292
-
291
+
293
292
  todos = {'lists':lists,'tasks':tasks}
294
293
  return todos
295
294
 
@@ -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
6
+ Exec=nowfocus -f
7
7
  Icon=nowfocus
8
8
  Terminal=false
9
9
  Type=Application
nowfocus/example-todo.txt CHANGED
@@ -5,9 +5,8 @@ Exercise
5
5
  Running
6
6
  Push-ups
7
7
  Yoga
8
- Set Up nowfocus
8
+ Set Up Nowfocus
9
9
  1 Open Settings
10
10
  3 Add your real Todo List(s)
11
11
  2 Set up your Time Tracker(s)
12
12
  4 Notice how numbering tasks "hoists" them
13
- 5 Refresh List
nowfocus/install.py ADDED
@@ -0,0 +1,92 @@
1
+ import os.path
2
+ import json
3
+ import importlib
4
+ from datetime import datetime, timezone
5
+ from pathlib import Path
6
+ import copy
7
+ import subprocess, sys
8
+ import shutil
9
+
10
+ import gi
11
+ gi.require_version('Gtk', '3.0')
12
+ from gi.repository import Gtk, GLib, Gdk, Gio
13
+
14
+ # Set working dir to file location
15
+ os.chdir(os.path.dirname(os.path.realpath(__file__)))
16
+
17
+ # Add working dir to path
18
+ sys.path.append(os.path.dirname(__file__))
19
+
20
+ import conf
21
+ from utils import *
22
+
23
+ def run_first_load_actions():
24
+ db_init()
25
+ create_default_timetracking_csv()
26
+ create_default_todo()
27
+ copy_desktop_integration_files()
28
+
29
+
30
+ def create_default_timetracking_csv():
31
+ target_file = conf.user_data_dir+'/nowfocus-timetracking-spreadsheet.csv'
32
+
33
+ data = 'date,duration,project,task,start time'
34
+
35
+ if not os.path.isfile(target_file):
36
+ with open(target_file, 'w') as file:
37
+ file.writelines(data)
38
+ print("Created default timetracker at "+target_file)
39
+ else:
40
+ print("Default timetracker already exists at "+target_file)
41
+
42
+
43
+ def create_default_todo():
44
+ source_file = 'example-todo.txt'
45
+ target_file = conf.user_data_dir+'/nowfocus-todo.txt'
46
+
47
+ if not os.path.isfile(target_file):
48
+ with open(source_file, 'r') as file:
49
+ data = file.readlines()
50
+
51
+ with open(target_file, 'w') as file:
52
+ file.writelines(data)
53
+
54
+
55
+ def db_init():
56
+ if not os.path.isfile(conf.db_file):
57
+ print('initializing database')
58
+
59
+ db_query("CREATE TABLE lists (id TEXT, label TEXT DEFAULT '', parent_id TEXT DEFAULT '', parent_label TEXT DEFAULT '', todolist TEXT DEFAULT '', priority INTEGER DEFAULT 0, status INTEGER DEFAULT 1, extended_label TEXT DEFAULT '', data TEXT DEFAULT '{}');")
60
+
61
+ db_query("CREATE TABLE tasks (id TEXT, label TEXT DEFAULT '', parent_id TEXT DEFAULT '', parent_label TEXT DEFAULT '', todolist TEXT DEFAULT '', priority INTEGER DEFAULT 0, status INTEGER DEFAULT 1, extended_label TEXT, data TEXT DEFAULT '{}');")
62
+
63
+ # , tags TEXT
64
+
65
+ db_query("CREATE TABLE sessions (start_time TEXT, duration INTEGER, task_id TEXT, parent_id TEXT, todolist TEXT, extended_label TEXT, notes TEXT, timetracker TEXT);")
66
+
67
+ db_query("CREATE TABLE system (field TEXT PRIMARY KEY NOT NULL, value TEXT);")
68
+
69
+ db_query("INSERT INTO system(field, value) VALUES('db_schema_version', '0.4')")
70
+
71
+
72
+ def copy_desktop_integration_files():
73
+
74
+ home = GLib.get_home_dir()
75
+
76
+ files = (
77
+ (home+'/.local/share/icons/hicolor/scalable/apps','nowfocus.svg'),
78
+ (home+'/.local/share/icons','nowfocus.png'),
79
+ (home+'/.local/share/applications/','nowfocus.desktop'),
80
+ (home+'/.config/autostart/','nowfocus.desktop')
81
+ )
82
+
83
+
84
+ for file in files:
85
+ try:
86
+ Path(file[0]).mkdir(parents=True, exist_ok=True)
87
+ shutil.copy("desktop-extras/"+file[1],file[0])
88
+ print("Copied "+file[1]+" to "+file[0])
89
+
90
+ except Exception as e:
91
+ print("Error Copying "+file[1]+" to "+file[0],e)
92
+
@@ -139,7 +139,7 @@ class SessionOptionsDialog(Gtk.Dialog):
139
139
 
140
140
  print(' start_time + ', session['start_time'])
141
141
 
142
- # Or change session[duration] value to reflect dropped / afk session
142
+ # Or change session[duration] value to reflect dropped session
143
143
  if response == 3:
144
144
  # Create custom end time
145
145
  custom_end_time = session['start_time'] + timedelta(seconds = (self.duration_input.get_value_as_int() * 60))
nowfocus/task_window.py CHANGED
@@ -80,7 +80,6 @@ class TaskWindow(Gtk.Window):
80
80
 
81
81
  self.l_sidebar = Gtk.VBox(spacing=5)
82
82
  self.l_sidebar.get_style_context().add_class("subtle")
83
-
84
83
  self.outer_box.add(self.l_sidebar)
85
84
 
86
85
  self.center_box = Gtk.VBox(spacing=10)
@@ -94,18 +93,11 @@ class TaskWindow(Gtk.Window):
94
93
 
95
94
  self.recreate_header()
96
95
 
97
- self.r_sidebar_outer = Gtk.ScrolledWindow()
98
- self.r_sidebar_outer.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
99
- self.outer_box.add(self.r_sidebar_outer)
100
96
 
101
97
  self.r_sidebar = Gtk.VBox(spacing=5)
102
98
  self.r_sidebar.get_style_context().add_class("subtle")
103
-
104
- self.r_sidebar.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
105
- self.r_sidebar.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK)
106
- self.r_sidebar.connect("enter-notify-event",self.test)
107
-
108
- self.r_sidebar_outer.add(self.r_sidebar)
99
+ self.outer_box.add(self.r_sidebar)
100
+
109
101
 
110
102
  self.tick_timer = GLib.timeout_add_seconds(1, self.tick)
111
103
 
@@ -125,15 +117,15 @@ class TaskWindow(Gtk.Window):
125
117
 
126
118
  if passed_data:
127
119
  dbg('taskwindow passed_data',passed_data,s='taskwindow')
128
- if 'afk_time' in passed_data:
120
+ if 'user_inactive_time' in passed_data:
129
121
 
130
- last_active_time = datetime.now() - timedelta(seconds=passed_data['afk_time'])
122
+ last_active_time = datetime.now() - timedelta(seconds=passed_data['user_inactive_time'])
131
123
 
132
124
  last_active_str = time.strftime('%H:%M', last_active_time.timetuple())
133
125
 
134
- afk_label = Gtk.Label()
135
- afk_label.set_markup("<b>Inactive Since "+" "+str(last_active_str) +"</b>")
136
- self.header.add(afk_label)
126
+ user_inactive_label = Gtk.Label()
127
+ user_inactive_label.set_markup("<b>Inactive Since "+" "+str(last_active_str) +"</b>")
128
+ self.header.add(user_inactive_label)
137
129
 
138
130
  pause_then_button = Gtk.Button(label="Finish Then")
139
131
 
@@ -230,6 +222,7 @@ class TaskWindow(Gtk.Window):
230
222
 
231
223
  self.connect("destroy", self.on_destroy)
232
224
 
225
+
233
226
  def test(self=None, one=None, two=None, **kwargs):
234
227
  print('task_window test')
235
228
  print(one)
@@ -385,24 +378,48 @@ class TaskWindow(Gtk.Window):
385
378
  except Exception:
386
379
  pass
387
380
 
381
+
388
382
  def create_sidebars(self):
389
383
  timeit()
384
+
385
+ self.l_sidebar.foreach(lambda child: child.destroy())
386
+ self.r_sidebar.foreach(lambda child: child.destroy())
387
+
390
388
  if conf.user['open_task_window_fullscreen'] and conf.user['show_task_window_sidebars']:
391
- self.r_sidebar_outer.set_size_request(350, -1)
392
389
  self.l_sidebar.set_size_request(350, -1)
390
+ self.r_sidebar.set_size_request(350, -1)
393
391
 
394
- self.SessionEditDialog = SessionEditDialog # passed to show_sessions
392
+ focus_percent_label = Gtk.Label()
393
+ focus_percent_label.set_markup("<b>Focus: "+str(utils.get_percent_time_focused())+"%</b>")
394
+ self.r_sidebar.pack_start(focus_percent_label, False, True, 25)
395
395
 
396
- #show lists on left
397
- self.l_sidebar.foreach(lambda child: child.destroy())
396
+ self.sessions_scrolledwindow = Gtk.ScrolledWindow()
397
+ self.sessions_scrolledwindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
398
+ self.r_sidebar.pack_start(self.sessions_scrolledwindow, True, True, 0)
399
+
400
+ self.sessions_box = Gtk.VBox(spacing=5)
401
+ # self.sessions_box.get_style_context().add_class("subtle")
402
+ self.sessions_scrolledwindow.add(self.sessions_box)
403
+
404
+ # self.r_sidebar.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
405
+ # self.r_sidebar.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK)
406
+ # self.r_sidebar.connect("enter-notify-event",self.test)
407
+
398
408
 
399
409
  self.l_sidebar.add(choose_from_lists(self.select_list_callback, 'None', None, False))
400
410
 
401
- #show sessions on right
402
- show_sessions(None, self, self.r_sidebar, None, 'start_time', '30',None, 30)
411
+ self.SessionEditDialog = SessionEditDialog # passed to show_sessions
412
+ show_sessions(None, self, self.sessions_box, None, 'start_time', '30',None, 30)
413
+
403
414
  self.l_sidebar.show_all()
404
415
  self.r_sidebar.show_all()
405
- timeit()
416
+ else:
417
+ self.r_sidebar.set_size_request(-1, -1)
418
+ self.l_sidebar.set_size_request(-1, -1)
419
+
420
+ self.l_sidebar.hide()
421
+ self.r_sidebar.hide()
422
+ timeit()
406
423
 
407
424
 
408
425
  def on_notes_changed(self,buffer):
@@ -628,11 +645,14 @@ class TaskWindow(Gtk.Window):
628
645
  self.unfullscreen()
629
646
  self.get_style_context().remove_class("large")
630
647
  conf.user['open_task_window_fullscreen'] = False
648
+ self.create_sidebars()
631
649
 
632
650
  else:
633
651
  self.fullscreen()
634
652
  self.get_style_context().add_class("large")
635
653
  conf.user['open_task_window_fullscreen'] = True
654
+ self.create_sidebars()
655
+
636
656
 
637
657
  utils.save_user_settings()
638
658
 
nowfocus/utils.py CHANGED
@@ -29,8 +29,6 @@ notify.init(conf.app_name)
29
29
 
30
30
  lists = {}
31
31
 
32
- db_file = conf.user_data_dir+"/data.db"
33
-
34
32
 
35
33
  def dbg(*data, **kwargs):
36
34
  ''' Any number of positional args then kwargs
@@ -318,7 +316,7 @@ def first(i, default = None):
318
316
  def save_user_settings():
319
317
  print("Save updated user_settings")
320
318
  dbg(conf.user)
321
- with open(conf.user_settings_dir+"/user_settings.json","w") as settings_file:
319
+ with open(conf.settings_file,"w") as settings_file:
322
320
  json.dump(conf.user, settings_file)
323
321
 
324
322
 
@@ -326,8 +324,7 @@ def db_query(sql,parameters=None,key=None,error_handling=1):
326
324
  '''error_handling: 2 = raise error, 1 = notify error, 0 = ignore errors '''
327
325
  # print('db_query parameters type: ',type(parameters))
328
326
  # print('first(parameters)',first(parameters))
329
- global db_file
330
- c = sqlite3.connect(db_file)
327
+ c = sqlite3.connect(conf.db_file)
331
328
  c.row_factory = sqlite3.Row
332
329
  try:
333
330
  with c:
@@ -368,23 +365,6 @@ def db_query(sql,parameters=None,key=None,error_handling=1):
368
365
  return []
369
366
 
370
367
 
371
- def db_init():
372
- if not os.path.isfile(db_file):
373
- print('initializing database')
374
-
375
- db_query("CREATE TABLE lists (id TEXT, label TEXT DEFAULT '', parent_id TEXT DEFAULT '', parent_label TEXT DEFAULT '', todolist TEXT DEFAULT '', priority INTEGER DEFAULT 0, status INTEGER DEFAULT 1, extended_label TEXT DEFAULT '', data TEXT DEFAULT '{}');")
376
-
377
- db_query("CREATE TABLE tasks (id TEXT, label TEXT DEFAULT '', parent_id TEXT DEFAULT '', parent_label TEXT DEFAULT '', todolist TEXT DEFAULT '', priority INTEGER DEFAULT 0, status INTEGER DEFAULT 1, extended_label TEXT, data TEXT DEFAULT '{}');")
378
-
379
- # , tags TEXT
380
-
381
- db_query("CREATE TABLE sessions (start_time TEXT, duration INTEGER, task_id TEXT, parent_id TEXT, todolist TEXT, extended_label TEXT, notes TEXT, timetracker TEXT);")
382
-
383
- db_query("CREATE TABLE system (field TEXT PRIMARY KEY NOT NULL, value TEXT);")
384
-
385
- db_query("INSERT INTO system(field, value) VALUES('db_schema_version', '0.4')")
386
-
387
-
388
368
  def db_schema_update():
389
369
 
390
370
  try:
@@ -464,7 +444,7 @@ def reindex(t=None):
464
444
 
465
445
  def reindex_all():
466
446
 
467
- db_query("DROP TABLE taskindex",None,None,0)
447
+ db_query("DROP TABLE IF EXISTS taskindex",None,None,0)
468
448
  db_query("CREATE VIRTUAL TABLE taskindex USING fts5(id, extended_label, priority, status)")
469
449
  db_query("INSERT INTO taskindex(id, extended_label, priority, status) SELECT id, extended_label, priority, status FROM tasks WHERE tasks.status IS NOT '-1' ")
470
450
 
@@ -903,6 +883,19 @@ def get_total_time(id, category = 'tasks', start_time = None, end_time = None, g
903
883
  return result
904
884
 
905
885
 
886
+ def get_percent_time_focused():
887
+
888
+ rand_seconds = db_query("SELECT SUM(duration) as seconds FROM sessions WHERE sessions.task_id = 'Randomness' AND "+ sessions_timeframe_sql())[0]['seconds']
889
+
890
+ focus_seconds = db_query("SELECT SUM(duration) as seconds FROM sessions WHERE sessions.task_id != 'Randomness' AND "+ sessions_timeframe_sql())[0]['seconds']
891
+
892
+ percent = round(divide(focus_seconds,(focus_seconds + rand_seconds)) * 100, 2)
893
+
894
+ dbg('get_percent_time_focused', str(percent)+"%", 'focus_seconds', focus_seconds, 'rand_seconds', rand_seconds)
895
+
896
+ return percent
897
+
898
+
906
899
  def get_recent_tasks(count = 15):
907
900
  data = db_query("SELECT DISTINCT tasks.* FROM tasks JOIN sessions ON sessions.task_id = tasks.id WHERE tasks.status = 1 GROUP BY tasks.id ORDER BY MAX(sessions.start_time) DESC LIMIT ?",(count,))
908
901
  o = {}
@@ -964,6 +957,25 @@ def force_number(i):
964
957
  return o
965
958
 
966
959
 
960
+ def divide(a, b):
961
+ a = force_number(a)
962
+ b = force_number(b)
963
+
964
+ if a == 0 or b == 0:
965
+ return 0
966
+ else:
967
+ return (a / b)
968
+
969
+ def clamp(value, min_limit, max_limit):
970
+ return max(min_limit, min(value, max_limit))
971
+
972
+
973
+ def invert_number_scale(i, max=5):
974
+ ''' Flips a positive number within a given range '''
975
+ o = abs(i - max )
976
+ return o
977
+
978
+
967
979
  def num_is_multiple_of(i,devisor = 2):
968
980
  try:
969
981
  i = int(i)
@@ -1157,7 +1169,11 @@ def choose_from_lists(callback, selected_list_id = None, session = None, accepts
1157
1169
  # new buttongroup for new todolist
1158
1170
  todolist = l['todolist']
1159
1171
  l['header'] = Gtk.Label()
1160
- l['header'].set_markup("<b>"+conf.user['todolists'][todolist]["label"]+"</b>")
1172
+ try:
1173
+ l['header'].set_markup("<b>"+conf.user['todolists'][todolist]["label"]+"</b>")
1174
+ except Exception as e:
1175
+ dbg('Exception creating button group header for list',l, e=e, l=0)
1176
+
1161
1177
  box.add(l['header'])
1162
1178
 
1163
1179
  if l['id'] == selected_list_id:
@@ -1216,7 +1232,7 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
1216
1232
  # separator after priority tasks
1217
1233
  target_menu.prepend(Gtk.SeparatorMenuItem.new())
1218
1234
 
1219
- # Create list sub menus
1235
+ # Create menu for each list
1220
1236
  for list_id, l in lists.items():
1221
1237
  if l['status'] != 1:
1222
1238
  continue
@@ -1228,19 +1244,25 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
1228
1244
  list_menu_items[list_id] = Gtk.MenuItem.new_with_label(lists[list_id]['label']) # the "item" that gets added
1229
1245
  list_menu_items[list_id].set_submenu(list_menus[list_id])
1230
1246
 
1231
- # Add sub menus (items) to parents (sub_menus)
1247
+ # Add each list to parents (sub_menus)
1232
1248
  for list_id, l in lists.items():
1233
1249
  if l['status'] == 1:
1234
1250
 
1235
- if not toplevel_todos and l['id'] == l['todolist']:
1236
- continue
1251
+ try:
1252
+
1253
+ if not toplevel_todos and l['id'] == l['todolist']:
1254
+ continue
1255
+
1256
+ if 'priority' in l and l['priority']:
1257
+ target_menu.prepend(list_menu_items[list_id])
1258
+ elif (toplevel_todos and l['id'] == l['todolist']) or (not toplevel_todos and l['parent_id'] == l['todolist']):
1259
+ target_menu.append(list_menu_items[list_id])
1260
+ else:
1261
+ list_menus[l['parent_id']].append(list_menu_items[list_id])
1237
1262
 
1238
- if 'priority' in l and l['priority']:
1239
- target_menu.prepend(list_menu_items[list_id])
1240
- elif (toplevel_todos and l['id'] == l['todolist']) or (not toplevel_todos and l['parent_id'] == l['todolist']):
1241
- target_menu.append(list_menu_items[list_id])
1242
- else:
1243
- list_menus[l['parent_id']].append(list_menu_items[list_id])
1263
+ except Exception as e:
1264
+ dbg('Error adding list to menu',l,e=e,l=0)
1265
+
1244
1266
 
1245
1267
 
1246
1268
  for id, t in tasks.items():
@@ -1288,27 +1310,29 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
1288
1310
 
1289
1311
  target_menu.show_all()
1290
1312
 
1313
+
1291
1314
  def datetime_minus_calendar_unit(unit = 'days', num = 1, ref_date = None):
1292
1315
  ''' returns a datetime for the start of the unit num units ago (from the optional ref_date)
1293
1316
  unit can be days, weeks, months, or years (plural or singular)
1294
-
1295
1317
  '''
1296
- # Looks weird but...
1318
+
1297
1319
  num = num - 1
1320
+
1298
1321
  if not ref_date:
1299
1322
  ref_date = start_of_day()
1323
+
1300
1324
  if unit in ['day','days']:
1301
1325
  o = ref_date - timedelta(days=num)
1302
- if num > 0: o = o - timedelta(days=num)
1303
- if unit in ['week','weeks']:
1326
+ elif unit in ['week','weeks']:
1304
1327
  o = ref_date - timedelta(days=ref_date.weekday())
1305
1328
  if num > 0: o = o + relativedelta(dt1= o, weeks= - num)
1306
- if unit in ['month','months']:
1329
+ elif unit in ['month','months']:
1307
1330
  o = ref_date.replace(day=1)
1308
1331
  if num > 0: o = o + relativedelta(dt1= o, months= - num)
1309
- if unit in ['year','years']:
1332
+ elif unit in ['year','years']:
1310
1333
  o = ref_date.replace(month=1,day=1)
1311
1334
  if num > 0: o = o + relativedelta(dt1= o, years= - num)
1335
+
1312
1336
  return o
1313
1337
 
1314
1338
  def get_time_target_data(i, item_type = 'tasks'):
@@ -1359,11 +1383,11 @@ def hours_search_timeframes(frame_name = None):
1359
1383
  ''' returns a tuple with start and end time for given frame (if one is provided) or a dict of frame_options.
1360
1384
  '''
1361
1385
 
1362
- default = "this year"
1386
+ default = "all time"
1363
1387
 
1364
1388
  frames = {
1365
1389
  'today':(datetime_minus_calendar_unit('day',1),now()),
1366
- 'yesterday':(datetime_minus_calendar_unit('day',1)),
1390
+ 'yesterday':(datetime_minus_calendar_unit('day',2),datetime_minus_calendar_unit('day',1)),
1367
1391
  'this week':(datetime_minus_calendar_unit('week',1),now()),
1368
1392
  'last week':(datetime_minus_calendar_unit('week',2),datetime_minus_calendar_unit('week',1)),
1369
1393
  '7 days':(datetime_minus_calendar_unit('days',7),now()),
@@ -1392,4 +1416,6 @@ def sessions_timeframe_sql():
1392
1416
  timeframe = hours_search_timeframes(conf.user['hours_search_timeframe'])
1393
1417
 
1394
1418
  timeframe_sql = " sessions.start_time > '"+timeframe[0].strftime("%Y-%m-%d %H:%M:%S")+"' AND sessions.start_time < '"+timeframe[1].strftime("%Y-%m-%d %H:%M:%S")+"' "
1419
+
1420
+ # dbg('sessions_timeframe_sql',timeframe_sql,s='search')
1395
1421
  return timeframe_sql
@@ -0,0 +1,20 @@
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