nowfocus 0.4.0__py3-none-any.whl → 0.4.2__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 +14 -11
- nowfocus/connectors/vikunja.py +16 -17
- nowfocus/task_window.py +37 -17
- nowfocus/utils.py +66 -20
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.2.dist-info}/METADATA +21 -20
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.2.dist-info}/RECORD +10 -10
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.2.dist-info}/WHEEL +0 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.2.dist-info}/entry_points.txt +0 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.2.dist-info}/top_level.txt +0 -0
nowfocus/__main__.py
CHANGED
|
@@ -57,6 +57,8 @@ setproctitle.setproctitle(conf.app_name)
|
|
|
57
57
|
print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file__)))
|
|
58
58
|
|
|
59
59
|
class Application(Gtk.Application):
|
|
60
|
+
icon_tick_number = 0
|
|
61
|
+
|
|
60
62
|
def __init__(self, *args, **kwargs):
|
|
61
63
|
super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
|
|
62
64
|
|
|
@@ -137,7 +139,6 @@ class Application(Gtk.Application):
|
|
|
137
139
|
|
|
138
140
|
|
|
139
141
|
|
|
140
|
-
|
|
141
142
|
def print_time_totals(self = None, widget = None):
|
|
142
143
|
|
|
143
144
|
# SELECT extended_label, (SUM(duration) / 60 ) FROM sessions GROUP BY extended_label
|
|
@@ -277,10 +278,7 @@ class Application(Gtk.Application):
|
|
|
277
278
|
notify.Notification.new("Good job on doing "+self.session['label'],"You've reached your target of "+str(t['value'])+" minutes "+str(t['within_value'])+" "+ t['within_unit'], None).show()
|
|
278
279
|
|
|
279
280
|
playsound('sound/xylophone-chord.mp3',False)
|
|
280
|
-
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
icon_tick_number = 0
|
|
284
282
|
|
|
285
283
|
def tick(self):
|
|
286
284
|
|
|
@@ -306,19 +304,24 @@ class Application(Gtk.Application):
|
|
|
306
304
|
|
|
307
305
|
label = self.session['label'] + ": " + sec_to_time(duration)
|
|
308
306
|
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
icon = 'icon-'+str(self.icon_tick_number)+'.svg'
|
|
308
|
+
|
|
311
309
|
else:
|
|
312
310
|
|
|
313
|
-
label = conf.user['default_text']
|
|
314
|
-
|
|
315
|
-
|
|
311
|
+
label = conf.user['default_text'] +" "+ sec_to_time(duration)
|
|
312
|
+
|
|
313
|
+
if duration > 60 and num_is_multiple_of(duration, 2):
|
|
314
|
+
icon = 'icon-red.svg'
|
|
315
|
+
|
|
316
316
|
else:
|
|
317
|
-
|
|
317
|
+
icon = 'icon-1.svg'
|
|
318
318
|
|
|
319
319
|
# https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
|
|
320
|
+
|
|
321
|
+
self.indicator.set_icon_full(os.path.abspath('icon/'+icon),label)
|
|
320
322
|
self.indicator.set_label(label,label)
|
|
321
323
|
|
|
324
|
+
|
|
322
325
|
if num_is_multiple_of(self.icon_tick_number,3):
|
|
323
326
|
self.refresh_all_changed_todo_files()
|
|
324
327
|
|
|
@@ -580,7 +583,7 @@ class Application(Gtk.Application):
|
|
|
580
583
|
|
|
581
584
|
def async_refresh(self, w=None, single_todo = None):
|
|
582
585
|
|
|
583
|
-
self.indicator.set_label("Refreshing Todolists", "
|
|
586
|
+
self.indicator.set_label("Refreshing Todolists", "Refreshing Todolists")
|
|
584
587
|
menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
|
|
585
588
|
self.menu.append(menu_item)
|
|
586
589
|
self.menu.show_all()
|
nowfocus/connectors/vikunja.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
286
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
397
|
-
self.
|
|
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
|
-
#
|
|
402
|
-
show_sessions(None, self, self.
|
|
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
|
-
|
|
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
|
@@ -464,7 +464,7 @@ def reindex(t=None):
|
|
|
464
464
|
|
|
465
465
|
def reindex_all():
|
|
466
466
|
|
|
467
|
-
db_query("DROP TABLE taskindex",None,None,0)
|
|
467
|
+
db_query("DROP TABLE IF EXISTS taskindex",None,None,0)
|
|
468
468
|
db_query("CREATE VIRTUAL TABLE taskindex USING fts5(id, extended_label, priority, status)")
|
|
469
469
|
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
470
|
|
|
@@ -903,6 +903,19 @@ def get_total_time(id, category = 'tasks', start_time = None, end_time = None, g
|
|
|
903
903
|
return result
|
|
904
904
|
|
|
905
905
|
|
|
906
|
+
def get_percent_time_focused():
|
|
907
|
+
|
|
908
|
+
rand_seconds = db_query("SELECT SUM(duration) as seconds FROM sessions WHERE sessions.task_id = 'Randomness' AND "+ sessions_timeframe_sql())[0]['seconds']
|
|
909
|
+
|
|
910
|
+
focus_seconds = db_query("SELECT SUM(duration) as seconds FROM sessions WHERE sessions.task_id != 'Randomness' AND "+ sessions_timeframe_sql())[0]['seconds']
|
|
911
|
+
|
|
912
|
+
percent = round(divide(focus_seconds,(focus_seconds + rand_seconds)) * 100, 2)
|
|
913
|
+
|
|
914
|
+
dbg('get_percent_time_focused', str(percent)+"%", 'focus_seconds', focus_seconds, 'rand_seconds', rand_seconds)
|
|
915
|
+
|
|
916
|
+
return percent
|
|
917
|
+
|
|
918
|
+
|
|
906
919
|
def get_recent_tasks(count = 15):
|
|
907
920
|
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
921
|
o = {}
|
|
@@ -964,6 +977,25 @@ def force_number(i):
|
|
|
964
977
|
return o
|
|
965
978
|
|
|
966
979
|
|
|
980
|
+
def divide(a, b):
|
|
981
|
+
a = force_number(a)
|
|
982
|
+
b = force_number(b)
|
|
983
|
+
|
|
984
|
+
if a == 0 or b == 0:
|
|
985
|
+
return 0
|
|
986
|
+
else:
|
|
987
|
+
return (a / b)
|
|
988
|
+
|
|
989
|
+
def clamp(value, min_limit, max_limit):
|
|
990
|
+
return max(min_limit, min(value, max_limit))
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def invert_number_scale(i, max=5):
|
|
994
|
+
''' Flips a positive number within a given range '''
|
|
995
|
+
o = abs(i - max )
|
|
996
|
+
return o
|
|
997
|
+
|
|
998
|
+
|
|
967
999
|
def num_is_multiple_of(i,devisor = 2):
|
|
968
1000
|
try:
|
|
969
1001
|
i = int(i)
|
|
@@ -1157,7 +1189,11 @@ def choose_from_lists(callback, selected_list_id = None, session = None, accepts
|
|
|
1157
1189
|
# new buttongroup for new todolist
|
|
1158
1190
|
todolist = l['todolist']
|
|
1159
1191
|
l['header'] = Gtk.Label()
|
|
1160
|
-
|
|
1192
|
+
try:
|
|
1193
|
+
l['header'].set_markup("<b>"+conf.user['todolists'][todolist]["label"]+"</b>")
|
|
1194
|
+
except Exception as e:
|
|
1195
|
+
dbg('Exception creating button group header for list',l, e=e, l=0)
|
|
1196
|
+
|
|
1161
1197
|
box.add(l['header'])
|
|
1162
1198
|
|
|
1163
1199
|
if l['id'] == selected_list_id:
|
|
@@ -1216,7 +1252,7 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
|
|
|
1216
1252
|
# separator after priority tasks
|
|
1217
1253
|
target_menu.prepend(Gtk.SeparatorMenuItem.new())
|
|
1218
1254
|
|
|
1219
|
-
# Create
|
|
1255
|
+
# Create menu for each list
|
|
1220
1256
|
for list_id, l in lists.items():
|
|
1221
1257
|
if l['status'] != 1:
|
|
1222
1258
|
continue
|
|
@@ -1228,19 +1264,25 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
|
|
|
1228
1264
|
list_menu_items[list_id] = Gtk.MenuItem.new_with_label(lists[list_id]['label']) # the "item" that gets added
|
|
1229
1265
|
list_menu_items[list_id].set_submenu(list_menus[list_id])
|
|
1230
1266
|
|
|
1231
|
-
# Add
|
|
1267
|
+
# Add each list to parents (sub_menus)
|
|
1232
1268
|
for list_id, l in lists.items():
|
|
1233
1269
|
if l['status'] == 1:
|
|
1234
1270
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1271
|
+
try:
|
|
1272
|
+
|
|
1273
|
+
if not toplevel_todos and l['id'] == l['todolist']:
|
|
1274
|
+
continue
|
|
1275
|
+
|
|
1276
|
+
if 'priority' in l and l['priority']:
|
|
1277
|
+
target_menu.prepend(list_menu_items[list_id])
|
|
1278
|
+
elif (toplevel_todos and l['id'] == l['todolist']) or (not toplevel_todos and l['parent_id'] == l['todolist']):
|
|
1279
|
+
target_menu.append(list_menu_items[list_id])
|
|
1280
|
+
else:
|
|
1281
|
+
list_menus[l['parent_id']].append(list_menu_items[list_id])
|
|
1237
1282
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
target_menu.append(list_menu_items[list_id])
|
|
1242
|
-
else:
|
|
1243
|
-
list_menus[l['parent_id']].append(list_menu_items[list_id])
|
|
1283
|
+
except Exception as e:
|
|
1284
|
+
dbg('Error adding list to menu',l,e=e,l=0)
|
|
1285
|
+
|
|
1244
1286
|
|
|
1245
1287
|
|
|
1246
1288
|
for id, t in tasks.items():
|
|
@@ -1288,27 +1330,29 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
|
|
|
1288
1330
|
|
|
1289
1331
|
target_menu.show_all()
|
|
1290
1332
|
|
|
1333
|
+
|
|
1291
1334
|
def datetime_minus_calendar_unit(unit = 'days', num = 1, ref_date = None):
|
|
1292
1335
|
''' returns a datetime for the start of the unit num units ago (from the optional ref_date)
|
|
1293
1336
|
unit can be days, weeks, months, or years (plural or singular)
|
|
1294
|
-
|
|
1295
1337
|
'''
|
|
1296
|
-
|
|
1338
|
+
|
|
1297
1339
|
num = num - 1
|
|
1340
|
+
|
|
1298
1341
|
if not ref_date:
|
|
1299
1342
|
ref_date = start_of_day()
|
|
1343
|
+
|
|
1300
1344
|
if unit in ['day','days']:
|
|
1301
1345
|
o = ref_date - timedelta(days=num)
|
|
1302
|
-
|
|
1303
|
-
if unit in ['week','weeks']:
|
|
1346
|
+
elif unit in ['week','weeks']:
|
|
1304
1347
|
o = ref_date - timedelta(days=ref_date.weekday())
|
|
1305
1348
|
if num > 0: o = o + relativedelta(dt1= o, weeks= - num)
|
|
1306
|
-
|
|
1349
|
+
elif unit in ['month','months']:
|
|
1307
1350
|
o = ref_date.replace(day=1)
|
|
1308
1351
|
if num > 0: o = o + relativedelta(dt1= o, months= - num)
|
|
1309
|
-
|
|
1352
|
+
elif unit in ['year','years']:
|
|
1310
1353
|
o = ref_date.replace(month=1,day=1)
|
|
1311
1354
|
if num > 0: o = o + relativedelta(dt1= o, years= - num)
|
|
1355
|
+
|
|
1312
1356
|
return o
|
|
1313
1357
|
|
|
1314
1358
|
def get_time_target_data(i, item_type = 'tasks'):
|
|
@@ -1359,11 +1403,11 @@ def hours_search_timeframes(frame_name = None):
|
|
|
1359
1403
|
''' returns a tuple with start and end time for given frame (if one is provided) or a dict of frame_options.
|
|
1360
1404
|
'''
|
|
1361
1405
|
|
|
1362
|
-
default = "
|
|
1406
|
+
default = "all time"
|
|
1363
1407
|
|
|
1364
1408
|
frames = {
|
|
1365
1409
|
'today':(datetime_minus_calendar_unit('day',1),now()),
|
|
1366
|
-
'yesterday':(datetime_minus_calendar_unit('day',1)),
|
|
1410
|
+
'yesterday':(datetime_minus_calendar_unit('day',2),datetime_minus_calendar_unit('day',1)),
|
|
1367
1411
|
'this week':(datetime_minus_calendar_unit('week',1),now()),
|
|
1368
1412
|
'last week':(datetime_minus_calendar_unit('week',2),datetime_minus_calendar_unit('week',1)),
|
|
1369
1413
|
'7 days':(datetime_minus_calendar_unit('days',7),now()),
|
|
@@ -1392,4 +1436,6 @@ def sessions_timeframe_sql():
|
|
|
1392
1436
|
timeframe = hours_search_timeframes(conf.user['hours_search_timeframe'])
|
|
1393
1437
|
|
|
1394
1438
|
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")+"' "
|
|
1439
|
+
|
|
1440
|
+
# dbg('sessions_timeframe_sql',timeframe_sql,s='search')
|
|
1395
1441
|
return timeframe_sql
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nowfocus
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
7
|
+
Project-URL: repository, https://codeberg.org/AltruistEnterprises/nowfocus
|
|
7
8
|
Classifier: Programming Language :: Python :: 3
|
|
8
9
|
Classifier: Operating System :: OS Independent
|
|
9
10
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
@@ -40,12 +41,9 @@ Requires-Dist: vobject
|
|
|
40
41
|
Requires-Dist: x-wr-timezone
|
|
41
42
|
Dynamic: license-file
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
mainfont: sans-serif
|
|
45
|
-
---
|
|
46
|
-
<div align="center"><img src="https://gitlab.com/GitFr33/nowfocus/-/raw/main/nowfocus.svg" width="60" align="center">
|
|
44
|
+
<div align="center"><img src="https://www.nowfocus.org/android-chrome-192x192.png" width="60" align="center">
|
|
47
45
|
|
|
48
|
-
#
|
|
46
|
+
# <a href="https://www.nowfocus.org/">*Nowfocus*</a> <br> Open-source task timer for Linux
|
|
49
47
|
|
|
50
48
|
**Avoid multifailing. Master your to-do lists. Track your time.**
|
|
51
49
|
|
|
@@ -100,20 +98,16 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
|
|
|
100
98
|
|
|
101
99
|
## Installation
|
|
102
100
|
|
|
103
|
-
<!-- no longer needed: meson libdbus-glib-1-dev patchelf -->
|
|
104
101
|
<!-- # note: gir1.2-appindicator3-0.1 can be substituted for gir1.2-ayatanaappindicator3-0.1 -->
|
|
105
102
|
|
|
106
103
|
1. Run the following in terminal to install and setup:
|
|
107
104
|
```
|
|
108
105
|
# Install dependencies
|
|
109
|
-
sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev
|
|
110
|
-
|
|
111
|
-
# only needed X display (rather than wayland)
|
|
112
|
-
sudo apt install xprintidle
|
|
106
|
+
sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev xprintidle
|
|
113
107
|
|
|
114
108
|
# Set up pipx
|
|
115
109
|
pipx ensurepath
|
|
116
|
-
source
|
|
110
|
+
source ~/.bashrc
|
|
117
111
|
|
|
118
112
|
# Install nowfocus
|
|
119
113
|
pipx install nowfocus
|
|
@@ -174,15 +168,22 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
|
|
|
174
168
|
- Start with verbose logging use: `nowfocus -l 3`
|
|
175
169
|
- Start with targeted verbose logging use: `nowfocus -s trello`
|
|
176
170
|
|
|
177
|
-
<!--
|
|
178
|
-
TODO: test that this works!
|
|
179
|
-
## Development
|
|
180
171
|
|
|
181
|
-
|
|
172
|
+
## Reporting Issues
|
|
173
|
+
[Open an issue on Codeberg](https://codeberg.org/AltruistEnterprises/nowfocus/issues) (Please include as much detail as you can.)
|
|
182
174
|
|
|
183
|
-
- Clone this repo somewhere (referred to as `YOUR_INSTALL_PATH`)
|
|
184
|
-
- Change to `YOUR_INSTALL_PATH` directory with `cd YOUR_INSTALL_PATH/nowfocus`
|
|
185
|
-
- build python module with `python3 -m build` (this should be done in a venv and will require some dependecies...)
|
|
186
|
-
- pipx install -e --force YOUR_INSTALL_PATH/monotask/ -->
|
|
187
175
|
|
|
176
|
+
## Development
|
|
177
|
+
[Fork **nowfocus** source code on Codeberg (GPL)](https://codeberg.org/AltruistEnterprises/nowfocus/issues)
|
|
178
|
+
|
|
179
|
+
### Install From Source
|
|
180
|
+
```
|
|
181
|
+
git clone https://codeberg.org/AltruistEnterprises/nowfocus/nowfocus.git
|
|
182
|
+
cd nowfocus
|
|
183
|
+
python3 -m venv .venv/nowfocus-build
|
|
184
|
+
source .venv/nowfocus-build/bin/activate
|
|
185
|
+
pip install -r build-requirements.txt
|
|
186
|
+
python3 -m build
|
|
187
|
+
pipx install -e --force YOUR_INSTALL_PATH
|
|
188
|
+
```
|
|
188
189
|
<!--built with python + GTK -->
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
nowfocus/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
|
-
nowfocus/__main__.py,sha256=
|
|
2
|
+
nowfocus/__main__.py,sha256=UlZn9O7eSKJRDxG5GrYSfBmRAKX0yweq80sxz59pIXU,32401
|
|
3
3
|
nowfocus/conf.py,sha256=5YLSw3ZipFyq62rOQYruIo_yCPn_f8YoyLHWpUYiE3g,6942
|
|
4
4
|
nowfocus/example-todo.txt,sha256=HRaNcPB1k8cksTtJS1GPqMjOdOY3gAUTWiSL0_ip0q8,265
|
|
5
5
|
nowfocus/new_task_dialog.py,sha256=GG49tOAwXiUKAHeaKnCG8Q3SZlL5pJijRZEcSjMwQng,4350
|
|
@@ -8,9 +8,9 @@ nowfocus/session_options.py,sha256=QVwJA53U7qZsbLe-OFr6UuFeaquco_yps_CRXsQ2_q4,5
|
|
|
8
8
|
nowfocus/sessions.csv,sha256=kYpr06yQg_J86NQ4AiYw4RnQchcw3ouPKVYa1lYDUNo,39
|
|
9
9
|
nowfocus/settings.py,sha256=Ufabuqd90tTDlzXB68jsbDz7233CEMsjnmPzaTSEHLI,35150
|
|
10
10
|
nowfocus/styles.css,sha256=PG1SrLkwSSay8M2VKeRcE0UdK54ndsEDFnRLRkmP-9M,510
|
|
11
|
-
nowfocus/task_window.py,sha256=
|
|
11
|
+
nowfocus/task_window.py,sha256=o9O0vv-uTvNO6pelyJ8YRgMeJsiYQd_zqm6-9JMzjBY,28367
|
|
12
12
|
nowfocus/user_idle_time.py,sha256=kPZ_bhoBdVMIBJXn2602FUfS4r-u8FnTq5Ze9D5QfHE,2363
|
|
13
|
-
nowfocus/utils.py,sha256=
|
|
13
|
+
nowfocus/utils.py,sha256=fkixBjP3_vpxDENoRQcOL1LUDYJEONlZvoXeU4qSqZE,49784
|
|
14
14
|
nowfocus/connectors/activitywatch.py,sha256=QbkOmjIOiVwccWc2xhhePd0Abww5vEiVpCNjeqOyYGg,921
|
|
15
15
|
nowfocus/connectors/caldav.py,sha256=PeM_9yJC8W17L8Y5AyS75o6GfzTrPoMYKIvetND8T78,5089
|
|
16
16
|
nowfocus/connectors/csv.py,sha256=FwMpHM5lPIT90HKBCQUncpaW7zqFjlHjMwKR0-XWg-4,821
|
|
@@ -21,7 +21,7 @@ nowfocus/connectors/todo_template.py,sha256=R37fA2LXo8_LpWIgqozytI5RqIUjGggFHup2
|
|
|
21
21
|
nowfocus/connectors/todotxt.py,sha256=QCZjbIhY4Lm37YD0GsKJQUqbj7s3eYmZGgRwUCndZ5w,3857
|
|
22
22
|
nowfocus/connectors/trello.py,sha256=VqwnvHGXXcljmdf6kRZcE6sfeBQYhped_KVBEBOzWXM,6072
|
|
23
23
|
nowfocus/connectors/txt.py,sha256=iskJsw3dZnI4bIeEDtZCY-aQfKRKtoGATEJ0k13npxI,8125
|
|
24
|
-
nowfocus/connectors/vikunja.py,sha256=
|
|
24
|
+
nowfocus/connectors/vikunja.py,sha256=Lg1lgF3C5B606CUf3-U8pVtPt7Cp6IKkT5wFN779V8w,10799
|
|
25
25
|
nowfocus/desktop-extras/nowfocus.desktop,sha256=0kWsx0ZfvPbubGG1uuFSHxxYUw2GV9Ly_rtlboM1mak,294
|
|
26
26
|
nowfocus/desktop-extras/nowfocus.png,sha256=P5rn6-0EAJa2WXf4SJoaNtLRUfiV3LdsOroPKsR6GfA,15148
|
|
27
27
|
nowfocus/desktop-extras/nowfocus.svg,sha256=nps7naZzuhWWuKzQbpvxr9wLyzjmzMPzNHSBQMVetOo,2137
|
|
@@ -50,9 +50,9 @@ nowfocus/icon/settings.svg,sha256=fgkGJouPPtZLxZn2nr_5pEp9MdhRSRaW9mtdxhJHDuQ,39
|
|
|
50
50
|
nowfocus/sound/bell-xylophone-g.mp3,sha256=1OBcRWvD87AGNcq1uZFR8HqG0nanJykImERfVDVxHD4,53891
|
|
51
51
|
nowfocus/sound/dinner-bell.mp3,sha256=hjjO0xqA4uXpYw9KLwwlBnrVfRhVq1K5OXzwlMXhRn4,113620
|
|
52
52
|
nowfocus/sound/xylophone-chord.mp3,sha256=gwgBSqhMt5PMzT5N03Z6TvDgipQZfnkEz_o81Rq5Z1U,131806
|
|
53
|
-
nowfocus-0.4.
|
|
54
|
-
nowfocus-0.4.
|
|
55
|
-
nowfocus-0.4.
|
|
56
|
-
nowfocus-0.4.
|
|
57
|
-
nowfocus-0.4.
|
|
58
|
-
nowfocus-0.4.
|
|
53
|
+
nowfocus-0.4.2.dist-info/licenses/LICENSE,sha256=fSJzoHs1EOCwEd7FIyokFeGEma7NKmTVEdHkCr5OIV4,35127
|
|
54
|
+
nowfocus-0.4.2.dist-info/METADATA,sha256=MUKTtnWDa08P4w_Z0FmW3QvI9-Q6aA11bmNYV-duISU,7141
|
|
55
|
+
nowfocus-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
56
|
+
nowfocus-0.4.2.dist-info/entry_points.txt,sha256=RbYY19-irSoNVglNeNnL9D36cHft7aKsaEGEYoSH3pA,51
|
|
57
|
+
nowfocus-0.4.2.dist-info/top_level.txt,sha256=3uLd9BwmfarZwqVUxkSJuVwJ8qHzjThte8rt_UYG7tE,9
|
|
58
|
+
nowfocus-0.4.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|