nowfocus 0.2.8__py3-none-any.whl → 0.2.13__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/__init__.py +0 -67
- nowfocus/__main__.py +45 -27
- nowfocus/conf.py +12 -5
- nowfocus/connectors/todotxt.py +2 -19
- nowfocus/connectors/txt.py +51 -37
- nowfocus/connectors/vikunja.py +6 -4
- nowfocus/new_task_dialog.py +4 -1
- nowfocus/settings.py +55 -25
- nowfocus/task_window.py +16 -10
- nowfocus/utils.py +147 -22
- {nowfocus-0.2.8.dist-info → nowfocus-0.2.13.dist-info}/METADATA +57 -37
- {nowfocus-0.2.8.dist-info → nowfocus-0.2.13.dist-info}/RECORD +16 -16
- {nowfocus-0.2.8.dist-info → nowfocus-0.2.13.dist-info}/WHEEL +0 -0
- {nowfocus-0.2.8.dist-info → nowfocus-0.2.13.dist-info}/entry_points.txt +0 -0
- {nowfocus-0.2.8.dist-info → nowfocus-0.2.13.dist-info}/licenses/LICENSE +0 -0
- {nowfocus-0.2.8.dist-info → nowfocus-0.2.13.dist-info}/top_level.txt +0 -0
nowfocus/__init__.py
CHANGED
|
@@ -1,68 +1 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
def startup():
|
|
4
|
-
# print("startup ",sys.argv)
|
|
5
|
-
|
|
6
|
-
''' if called with no arguments send shutdown to pipe, wait 2 seconds delete pipe file and launch application. if called with arguments send args to pipe if it exist otherwise '''
|
|
7
|
-
|
|
8
|
-
parser = argparse.ArgumentParser(
|
|
9
|
-
# prog='ProgramName',
|
|
10
|
-
# description='What the program does',
|
|
11
|
-
# epilog='Text at the bottom of help'
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
parser.add_argument('task',nargs='?') # optional positional argument
|
|
15
|
-
parser.add_argument('-s', '--debug_systems', nargs="*", default=[]) # option that takes a value
|
|
16
|
-
parser.add_argument('-l', '--debug_level', default=1) # option that takes a value
|
|
17
|
-
parser.add_argument('-f', '--force', action='store_true', help="Force restart by deleting existing named pipe") # on/off flag
|
|
18
|
-
# parser.add_argument('-v', '--verbose', action='store_true') # on/off flag
|
|
19
|
-
|
|
20
|
-
args = parser.parse_args()
|
|
21
|
-
print(args)
|
|
22
|
-
|
|
23
|
-
conf.debug_level = int(args.debug_level)
|
|
24
|
-
conf.debug_systems = args.debug_systems
|
|
25
|
-
|
|
26
|
-
if args.force:
|
|
27
|
-
print("Lanched with --force flag, forcibly deleting old pipe")
|
|
28
|
-
try:
|
|
29
|
-
os.remove(pipe_file)
|
|
30
|
-
except Exception as e:
|
|
31
|
-
print(e)
|
|
32
|
-
|
|
33
|
-
try:
|
|
34
|
-
os.mkfifo(pipe_file)
|
|
35
|
-
|
|
36
|
-
signal.signal(signal.SIGUSR1, Application.signal_handler)
|
|
37
|
-
app = Application()
|
|
38
|
-
|
|
39
|
-
if args.task:
|
|
40
|
-
print("Writing args.task to pipe", args.task)
|
|
41
|
-
with open(pipe_file, "w") as pipeout:
|
|
42
|
-
pipeout.write(args.task)
|
|
43
|
-
pipeout.close()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
except FileExistsError:
|
|
47
|
-
print("Named pipe exists, application must be running (or improperly shut down.) ")
|
|
48
|
-
|
|
49
|
-
# if args: pass to pipe and exit
|
|
50
|
-
if args.task:
|
|
51
|
-
pipe_line = args.task
|
|
52
|
-
else:
|
|
53
|
-
pipe_line = "open_task_window"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
print("Writing arg ",pipe_line," to pipe")
|
|
57
|
-
|
|
58
|
-
with open(pipe_file, "w") as pipeout:
|
|
59
|
-
pipeout.write(pipe_line)
|
|
60
|
-
pipeout.close()
|
|
61
|
-
|
|
62
|
-
exit()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
except Exception as e:
|
|
67
|
-
print(f"Named pipe creation failed: {e}")
|
|
68
1
|
|
nowfocus/__main__.py
CHANGED
|
@@ -50,7 +50,6 @@ from session_options import SessionOptionsDialog
|
|
|
50
50
|
dbg(conf.user,l=3,s='user_settings')
|
|
51
51
|
|
|
52
52
|
setproctitle.setproctitle(conf.app_name)
|
|
53
|
-
pipe_file = os.path.dirname(os.path.realpath(__file__))+"/"+conf.app_id+"_pipe"
|
|
54
53
|
|
|
55
54
|
print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file__)))
|
|
56
55
|
|
|
@@ -106,6 +105,8 @@ class Application(Gtk.Application):
|
|
|
106
105
|
except Exception as e:
|
|
107
106
|
dbg("Error resuming session",e,l=1)
|
|
108
107
|
|
|
108
|
+
utils.start_todo_file_watchers()
|
|
109
|
+
|
|
109
110
|
self.pipethread = threading.Thread(target=self.check_pipe)
|
|
110
111
|
self.pipethread.daemon = True
|
|
111
112
|
self.pipethread.start()
|
|
@@ -163,11 +164,11 @@ class Application(Gtk.Application):
|
|
|
163
164
|
print('Caching active session', self.session['label'])
|
|
164
165
|
db_set_session_cache(self.session)
|
|
165
166
|
try:
|
|
166
|
-
# print("before os.remove(
|
|
167
|
-
os.remove(
|
|
167
|
+
# print("before os.remove(conf.pipe)")
|
|
168
|
+
os.remove(conf.pipe)
|
|
168
169
|
print("Pipe removed")
|
|
169
170
|
except Exception as e:
|
|
170
|
-
print("Error removing
|
|
171
|
+
print("Error removing conf.pipe in quit",e)
|
|
171
172
|
|
|
172
173
|
notify.uninit()
|
|
173
174
|
Gtk.main_quit()
|
|
@@ -217,6 +218,8 @@ class Application(Gtk.Application):
|
|
|
217
218
|
if (int(time_difference(conf.todo_sync_time)) / 60) > conf.user['todolist_refresh_interval'] * 60 :
|
|
218
219
|
self.async_refresh()
|
|
219
220
|
|
|
221
|
+
# TODO: use individual todo_refresh_times
|
|
222
|
+
|
|
220
223
|
minutes = (int(self.session['duration']) / 60)
|
|
221
224
|
|
|
222
225
|
if(self.is_running == False):
|
|
@@ -320,6 +323,13 @@ class Application(Gtk.Application):
|
|
|
320
323
|
# https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
|
|
321
324
|
indicator.set_label(label, "Wide")
|
|
322
325
|
|
|
326
|
+
for todo in conf.todo_sync_required:
|
|
327
|
+
# print('tick noticed a todo needing refreshment, time since refresh: ',time_difference(conf.todo_sync_times[todo]))
|
|
328
|
+
|
|
329
|
+
if time_difference(conf.todo_sync_times[todo]) > 4:
|
|
330
|
+
# print('tick noticed a todo needing refreshment')
|
|
331
|
+
self.async_refresh(None,conf.user['todolists'][todo])
|
|
332
|
+
conf.todo_sync_required = {}
|
|
323
333
|
return True
|
|
324
334
|
|
|
325
335
|
|
|
@@ -431,13 +441,23 @@ class Application(Gtk.Application):
|
|
|
431
441
|
|
|
432
442
|
def mark_done(self, w=None, task = None):
|
|
433
443
|
''' second (task) argument is required and must be a task object '''
|
|
434
|
-
|
|
435
|
-
print(task)
|
|
444
|
+
|
|
445
|
+
print("Mark Task done",task['label'])
|
|
446
|
+
# print(task)
|
|
436
447
|
|
|
437
448
|
todolist_conf = conf.user['todolists'][task['todolist']]
|
|
438
449
|
|
|
439
450
|
try:
|
|
451
|
+
|
|
440
452
|
done_thread = threading.Thread(target=conf.todo_connectors[todolist_conf['type']].mark_task_done, args=(task,) )
|
|
453
|
+
conf.todo_sync_times[todolist_conf['id']] = now() # this is to avoid causing a refresh, perhaps not the best though
|
|
454
|
+
|
|
455
|
+
# Other Options:
|
|
456
|
+
# make a custom class extending Thread with callback method that runs del conf.file_watch_ignores[todolist_conf['id']]
|
|
457
|
+
# Complicated
|
|
458
|
+
# deal with file_watch_ignores in the connector
|
|
459
|
+
# poor seperation
|
|
460
|
+
#
|
|
441
461
|
done_thread.start()
|
|
442
462
|
|
|
443
463
|
db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
|
|
@@ -454,7 +474,7 @@ class Application(Gtk.Application):
|
|
|
454
474
|
|
|
455
475
|
|
|
456
476
|
def stop_task(self, w = '', action = 'save', custom_end_time=None):
|
|
457
|
-
''' supported actions are save,cancel,mark_done '''
|
|
477
|
+
''' supported actions are save, cancel, mark_done '''
|
|
458
478
|
|
|
459
479
|
list_menus = self.list_menus
|
|
460
480
|
menu_tasks = self.menu_tasks
|
|
@@ -490,8 +510,8 @@ class Application(Gtk.Application):
|
|
|
490
510
|
# print("add ",utils.extended_label(task)," to recent tasks")
|
|
491
511
|
i = Gtk.MenuItem.new_with_label(utils.extended_label(task))
|
|
492
512
|
i.connect('activate',self.start_task,task)
|
|
493
|
-
list_menus['recent'].prepend(i)
|
|
494
513
|
try:
|
|
514
|
+
list_menus['recent'].prepend(i)
|
|
495
515
|
list_menus['recent'].get_children()[11].destroy()
|
|
496
516
|
except Exception as e:
|
|
497
517
|
dbg("Exception trying to rotate recent tasks. probably are less than 11",l=2,s="recent")
|
|
@@ -543,7 +563,6 @@ class Application(Gtk.Application):
|
|
|
543
563
|
i = Gtk.ImageMenuItem.new_with_label("Edit Session")
|
|
544
564
|
i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/edit.svg')))
|
|
545
565
|
|
|
546
|
-
# i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/status/software-installed-symbolic.svg'))
|
|
547
566
|
i.set_always_show_image(True)
|
|
548
567
|
i.connect("activate", self.open_session_options_dialog,'from_menu')
|
|
549
568
|
self.menu.insert(i,0)
|
|
@@ -551,33 +570,34 @@ class Application(Gtk.Application):
|
|
|
551
570
|
i = Gtk.ImageMenuItem.new_with_label("Pause" )
|
|
552
571
|
i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/pause.svg')))
|
|
553
572
|
|
|
554
|
-
# i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/multimedia/pause-symbolic.svg'))
|
|
555
573
|
i.set_always_show_image(True)
|
|
556
574
|
i.connect("activate", self.stop_task)
|
|
557
575
|
self.menu.insert(i,0)
|
|
558
576
|
|
|
559
577
|
|
|
560
|
-
def async_refresh(self, w=None):
|
|
578
|
+
def async_refresh(self, w=None, single_todo = None):
|
|
561
579
|
|
|
562
580
|
self.indicator.set_label("Refreshing Todolists", "Wide")
|
|
563
581
|
menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
|
|
564
582
|
self.menu.append(menu_item)
|
|
565
583
|
self.menu.show_all()
|
|
566
584
|
|
|
567
|
-
connectors_thread = threading.Thread(target=self.async_refresh_inner)
|
|
585
|
+
connectors_thread = threading.Thread(target=self.async_refresh_inner,args=(single_todo,))
|
|
568
586
|
connectors_thread.start()
|
|
569
587
|
|
|
570
588
|
|
|
571
|
-
def async_refresh_inner(self):
|
|
589
|
+
def async_refresh_inner(self, single_todo = None):
|
|
572
590
|
# dbg("async refresh started",s="todoloading",l=3)
|
|
573
|
-
|
|
591
|
+
if single_todo:
|
|
592
|
+
utils.refresh_todolist(single_todo)
|
|
593
|
+
utils.reindex()
|
|
594
|
+
|
|
595
|
+
else:
|
|
596
|
+
utils.get_todolists()
|
|
597
|
+
|
|
574
598
|
dbg("async refresh complete",s="todoloading",l=3)
|
|
575
599
|
GLib.idle_add(self.update_menu)
|
|
576
600
|
|
|
577
|
-
try:
|
|
578
|
-
GLib.idle_add(self.taskwindow.refresh_search_cache)
|
|
579
|
-
except Exception:
|
|
580
|
-
pass
|
|
581
601
|
|
|
582
602
|
|
|
583
603
|
def update_menu(self, w = ''):
|
|
@@ -727,10 +747,10 @@ class Application(Gtk.Application):
|
|
|
727
747
|
dbg("no handler for received signal",s='signals',l=3)
|
|
728
748
|
|
|
729
749
|
def check_pipe(self):
|
|
730
|
-
# print("Listening to pipe at ",
|
|
750
|
+
# print("Listening to pipe at ",conf.pipe)
|
|
731
751
|
|
|
732
752
|
try:
|
|
733
|
-
with open(
|
|
753
|
+
with open(conf.pipe, "r") as pipe:
|
|
734
754
|
data = pipe.read().strip()
|
|
735
755
|
|
|
736
756
|
print("check_pipe ")
|
|
@@ -739,10 +759,8 @@ class Application(Gtk.Application):
|
|
|
739
759
|
pipe.close()
|
|
740
760
|
|
|
741
761
|
# TODO: add registry of special commands
|
|
762
|
+
# How to handle function arguments? (for example refresh a todolist with it's id)
|
|
742
763
|
# if data in ['quit','open_task_window']:
|
|
743
|
-
# TODO: how to all dynamic function name here?
|
|
744
|
-
# GLib.idle_add(self.open_task_window)
|
|
745
|
-
|
|
746
764
|
|
|
747
765
|
if data == 'quit':
|
|
748
766
|
GLib.idle_add(self.quit)
|
|
@@ -795,13 +813,13 @@ def startup():
|
|
|
795
813
|
if args.force:
|
|
796
814
|
print("Lanched with --force flag, forcibly deleting old pipe")
|
|
797
815
|
try:
|
|
798
|
-
os.remove(
|
|
816
|
+
os.remove(conf.pipe)
|
|
799
817
|
except Exception as e:
|
|
800
818
|
print(e)
|
|
801
819
|
|
|
802
820
|
|
|
803
821
|
try:
|
|
804
|
-
os.mkfifo(
|
|
822
|
+
os.mkfifo(conf.pipe)
|
|
805
823
|
dbg("Named pipe created successfully!", s="cli")
|
|
806
824
|
|
|
807
825
|
signal.signal(signal.SIGUSR1, Application.signal_handler)
|
|
@@ -809,7 +827,7 @@ def startup():
|
|
|
809
827
|
|
|
810
828
|
if args.task:
|
|
811
829
|
print("Writing args.task to pipe", args.task)
|
|
812
|
-
with open(
|
|
830
|
+
with open(conf.pipe, "w") as pipeout:
|
|
813
831
|
pipeout.write(args.task)
|
|
814
832
|
pipeout.close()
|
|
815
833
|
|
|
@@ -827,7 +845,7 @@ def startup():
|
|
|
827
845
|
|
|
828
846
|
dbg("Writing arg to pipe ",pipe_line, s="cli")
|
|
829
847
|
|
|
830
|
-
with open(
|
|
848
|
+
with open(conf.pipe, "w") as pipeout:
|
|
831
849
|
pipeout.write(pipe_line)
|
|
832
850
|
pipeout.close()
|
|
833
851
|
|
nowfocus/conf.py
CHANGED
|
@@ -22,20 +22,24 @@ Path(user_settings_dir).mkdir(parents=True, exist_ok=True)
|
|
|
22
22
|
|
|
23
23
|
debug_level = 1 # dev value
|
|
24
24
|
debug_systems = []
|
|
25
|
+
pipe = "/tmp/"+app_id+"-pipe" # Will that work?
|
|
25
26
|
|
|
26
27
|
# key and type must be the same, (Seems redundant but it's is quite helpful)
|
|
27
28
|
connectors = {
|
|
28
29
|
"todolists":{
|
|
29
|
-
'txt':{'id':'txt','type':'txt','label':'','file':'',"status":True,'timetracker':''},
|
|
30
|
+
'txt':{'id':'txt','type':'txt','label':'','file':'', "watch_file":True, "status":True,'timetracker':''},
|
|
30
31
|
|
|
31
32
|
'trello':{'id':'trello','type':'trello','label':'Trello','APIKey':'aecf1d7791b4f1a4bb7d6ca5827ba0d3', 'token':'',"status":True,'timetracker':''},
|
|
32
33
|
|
|
33
34
|
'vikunja':{'id':'vikunja','type':'vikunja','label':'Vikunja','url':'http://localhost:3456/', 'token':'',"username":"","status":True,'timetracker':''},
|
|
34
35
|
|
|
35
36
|
'caldav':{'id':'caldav','type':'caldav','label':'CalDav Todo','url':'http://localhost:3456/','password': "",'username': '',"status":True,'timetracker':''},
|
|
37
|
+
|
|
36
38
|
'psc_timetracker':{'id':'psc_timetracker','type':'psc_timetracker','label':'Timetracker','url':'https://photosynth.ca/timetracker/','key':'',"status":True,'timetracker':'Timetracker'},
|
|
39
|
+
|
|
37
40
|
'taskwarrior':{'id':'taskwarrior','type':'taskwarrior','label':'TaskWarrior',"status":True,'timetracker':''},
|
|
38
|
-
|
|
41
|
+
|
|
42
|
+
'todotxt':{'id':'todotxt','type':'todotxt','label':'','file':"", "watch_file":True, "status":True,'timetracker':''},
|
|
39
43
|
},
|
|
40
44
|
"timetrackers":{
|
|
41
45
|
'csv':{'id':'csv','type':'csv','label':'CSV file','file':'sessions.csv',"status":True},
|
|
@@ -46,18 +50,21 @@ connectors = {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
todo_sync_time = datetime.now()
|
|
53
|
+
todo_sync_times = {}
|
|
49
54
|
|
|
50
55
|
todo_sync_required = {}
|
|
56
|
+
file_watchers = {}
|
|
57
|
+
file_watch_ignores = {}
|
|
51
58
|
|
|
52
59
|
prototype_settings = {
|
|
53
|
-
"pomodoro_interval":
|
|
60
|
+
"pomodoro_interval": 40,
|
|
54
61
|
"open_task_window_fullscreen": True,
|
|
55
62
|
"randomness_interrupt_interval":5,
|
|
56
63
|
"default_text": "What am I doing?",
|
|
57
|
-
"todolist_refresh_interval":
|
|
64
|
+
"todolist_refresh_interval":1,
|
|
58
65
|
"version":0.2,
|
|
59
66
|
"display_todolist_as_top_level_list":'auto',
|
|
60
|
-
'max_top_level_menu_items':
|
|
67
|
+
'max_top_level_menu_items':10,
|
|
61
68
|
# 'tick_interval':1,
|
|
62
69
|
'hours_search_timeframe':'this year',
|
|
63
70
|
'invoice_hourly_rate':0,
|
nowfocus/connectors/todotxt.py
CHANGED
|
@@ -20,24 +20,6 @@ import utils
|
|
|
20
20
|
# }
|
|
21
21
|
# }
|
|
22
22
|
|
|
23
|
-
# def watch(user_conf):
|
|
24
|
-
# import pyinotify
|
|
25
|
-
|
|
26
|
-
# """Watch for modifications of the todo file with pyinotify."""
|
|
27
|
-
# wm = pyinotify.WatchManager()
|
|
28
|
-
# notifier = pyinotify.ThreadedNotifier(wm, mark_changed, user_conf)
|
|
29
|
-
# notifier.start()
|
|
30
|
-
|
|
31
|
-
# # wm.add_watch(os.path.dirname(user_conf['file']), pyinotify.IN_MODIFY | pyinotify.IN_MOVED_TO) # Watch whole folder. Perhaps more reliable
|
|
32
|
-
# wm.add_watch(user_conf['file'], pyinotify.IN_MODIFY | pyinotify.IN_MOVED_TO) # just the file. Perhaps better
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# def mark_changed(event, user_conf):
|
|
36
|
-
# ''' This is in a thread '''
|
|
37
|
-
# if event.pathname == user_conf['file']:
|
|
38
|
-
# conf.todo_sync_required[user_conf['id']] = True
|
|
39
|
-
|
|
40
|
-
|
|
41
23
|
|
|
42
24
|
def task_id(user_conf, t):
|
|
43
25
|
''' The task parameter must have at least "label", "todolist" and "parent_id" '''
|
|
@@ -71,7 +53,8 @@ def add_new_task(user_conf,list,task_label):
|
|
|
71
53
|
}
|
|
72
54
|
}
|
|
73
55
|
return t
|
|
74
|
-
|
|
56
|
+
|
|
57
|
+
|
|
75
58
|
def mark_task_done(task):
|
|
76
59
|
'''Return True on success False on error'''
|
|
77
60
|
|
nowfocus/connectors/txt.py
CHANGED
|
@@ -8,27 +8,36 @@ def add_new_task(user_conf,list,task_label):
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
with open(file_uri, 'r') as file:
|
|
11
|
-
# read a list of lines into data
|
|
12
11
|
data = file.readlines()
|
|
13
12
|
|
|
14
13
|
if list['id'] == user_conf['id']:
|
|
15
|
-
# Top level
|
|
16
|
-
new_line = task_label+"\n"
|
|
14
|
+
# Top level list, append to bottom
|
|
17
15
|
line_no = len(data) + 1
|
|
18
16
|
else:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
line_no = get_line_no(list,data) + 1
|
|
18
|
+
|
|
19
|
+
new_line = list['data']['indent']+task_label+"\n"
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
# # Old version without recorded indent
|
|
22
|
+
# if list['id'] == user_conf['id']:
|
|
23
|
+
# # Top level insert
|
|
24
|
+
# new_line = task_label+"\n"
|
|
25
|
+
# line_no = len(data) + 1
|
|
26
|
+
# else:
|
|
27
|
+
# Sub list insert
|
|
28
|
+
# TODO: Add it at the end of the list rather than the start ...
|
|
29
|
+
# Yup. rjust(indent + len)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# indent = len(list['data']['original_line']) - len(list['data']['original_line'].lstrip()) + 4 #BUG: check indent of next line instead of arbitrarily using 4 spaces
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
# new_line = task_label+"\n"
|
|
35
|
+
# # I'm sure there's a better way to add a bunch of spaces to to a string
|
|
36
|
+
# while indent > 0:
|
|
37
|
+
# new_line = " " + new_line
|
|
38
|
+
# indent = indent - 1
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
# line_no = get_line_no(list,data) + 1
|
|
32
41
|
|
|
33
42
|
data.insert(line_no,new_line)
|
|
34
43
|
|
|
@@ -64,17 +73,15 @@ def get_line_no(i,lines):
|
|
|
64
73
|
else:
|
|
65
74
|
|
|
66
75
|
line_no = 0 #TODO: Improve, (better foreach with key)
|
|
67
|
-
in_list = False
|
|
68
76
|
|
|
69
77
|
for line in lines:
|
|
70
78
|
|
|
71
|
-
# TODO: Find the list
|
|
79
|
+
# TODO: Find the list
|
|
72
80
|
if line == i['data']['original_line']:
|
|
73
81
|
return line_no
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw("Line not found error Ha!")
|
|
83
|
+
|
|
84
|
+
utils.dbg("Line not found error",i,lines,s='txt',l=0)
|
|
78
85
|
|
|
79
86
|
def task_id(task):
|
|
80
87
|
''' The task parameter must have at least "label", "todolist" and "parent_id" '''
|
|
@@ -120,17 +127,18 @@ def get_todos(user_conf):
|
|
|
120
127
|
tasks = {}
|
|
121
128
|
lists = {}
|
|
122
129
|
lists[user_conf['id']] = {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
'id':user_conf['id'],
|
|
131
|
+
'label':user_conf['label'],
|
|
132
|
+
'parent_id':'',
|
|
133
|
+
'parent_label':'',
|
|
134
|
+
'todolist':user_conf['id'],
|
|
135
|
+
'data':{
|
|
136
|
+
'line_no':0,
|
|
137
|
+
'original_line':'',
|
|
138
|
+
'accepts_tasks':True,
|
|
139
|
+
'indent':'',
|
|
140
|
+
}
|
|
141
|
+
}
|
|
134
142
|
|
|
135
143
|
current_list = []
|
|
136
144
|
current_list_label = []
|
|
@@ -149,7 +157,7 @@ def get_todos(user_conf):
|
|
|
149
157
|
# file_uri = "todo.txt"
|
|
150
158
|
line_no = -1
|
|
151
159
|
|
|
152
|
-
with open(file_uri) as file:
|
|
160
|
+
with open(file_uri,"r") as file:
|
|
153
161
|
for line in file:
|
|
154
162
|
|
|
155
163
|
line_no = line_no + 1
|
|
@@ -171,11 +179,13 @@ def get_todos(user_conf):
|
|
|
171
179
|
#TODO: use markdown title syntax as list name in addition to indentation
|
|
172
180
|
|
|
173
181
|
indent = len(line) - len(line.lstrip())
|
|
182
|
+
indent_str = line[0:indent]
|
|
174
183
|
|
|
175
184
|
if indent > prev_indent:
|
|
176
|
-
# prev item is a sub_list header,
|
|
185
|
+
# prev item is a sub_list header, copy it to lists
|
|
177
186
|
lists[prev_id] = tasks[prev_id]
|
|
178
|
-
|
|
187
|
+
lists[prev_id]['data']['indent'] = indent_str
|
|
188
|
+
# del tasks[prev_id]
|
|
179
189
|
current_list.append(prev_id)
|
|
180
190
|
current_list_label.append(prev_label)
|
|
181
191
|
current_indent.append(indent)
|
|
@@ -194,6 +204,13 @@ def get_todos(user_conf):
|
|
|
194
204
|
|
|
195
205
|
|
|
196
206
|
utils.dbg(line.rstrip(),s='txt',l=3)
|
|
207
|
+
|
|
208
|
+
# Get prioritized tasks (starting with a number)
|
|
209
|
+
if label.split()[0].isnumeric():
|
|
210
|
+
priority = float(label.split()[0])
|
|
211
|
+
label = label.removeprefix(label.split()[0]).strip()
|
|
212
|
+
else:
|
|
213
|
+
priority = '0'
|
|
197
214
|
|
|
198
215
|
task_id = file_uri+":"+"/".join(current_list_label)+":"+label
|
|
199
216
|
|
|
@@ -211,6 +228,7 @@ def get_todos(user_conf):
|
|
|
211
228
|
'parent_label':str(parent_label),
|
|
212
229
|
'todolist':user_conf['id'],
|
|
213
230
|
'status': status,
|
|
231
|
+
'priority': priority,
|
|
214
232
|
'data':{
|
|
215
233
|
'line_no':line_no,
|
|
216
234
|
'original_line':line,
|
|
@@ -218,11 +236,7 @@ def get_todos(user_conf):
|
|
|
218
236
|
}
|
|
219
237
|
}
|
|
220
238
|
|
|
221
|
-
|
|
222
|
-
# if label[0].isdigit()
|
|
223
|
-
if label.split()[0].isnumeric():
|
|
224
|
-
tasks[task_id]['priority'] = float(label.split()[0])
|
|
225
|
-
tasks[task_id]['label'] = label.removeprefix(label.split()[0]).strip()
|
|
239
|
+
|
|
226
240
|
|
|
227
241
|
prev_indent = indent
|
|
228
242
|
prev_label = label
|
nowfocus/connectors/vikunja.py
CHANGED
|
@@ -294,14 +294,16 @@ def get_todos(user_conf):
|
|
|
294
294
|
return todos
|
|
295
295
|
|
|
296
296
|
|
|
297
|
-
def launch(user_conf, item = None, category =
|
|
297
|
+
def launch(user_conf, item = None, category = 'task'):
|
|
298
298
|
''' Open Vikunja '''
|
|
299
|
+
utils.dbg("vikunja.py launch","user_conf", user_conf,"item",item,'category',category,l=3, s='vikunja')
|
|
300
|
+
|
|
299
301
|
url = user_conf['url']
|
|
300
302
|
|
|
301
|
-
if item
|
|
302
|
-
if category
|
|
303
|
+
if item:
|
|
304
|
+
if category in ('list','lists'):
|
|
303
305
|
url += 'projects/'+str(item['data']['id'])
|
|
304
|
-
|
|
306
|
+
else:
|
|
305
307
|
url += 'tasks/'+str(item['data']['id'])
|
|
306
308
|
|
|
307
309
|
utils.open_external(url)
|
nowfocus/new_task_dialog.py
CHANGED
|
@@ -77,7 +77,7 @@ class NewTaskWDialog(Gtk.Dialog):
|
|
|
77
77
|
|
|
78
78
|
if response == 1 or response == 2:
|
|
79
79
|
|
|
80
|
-
task_label = self.task_label_entry.get_text()
|
|
80
|
+
task_label = self.task_label_entry.get_text().strip()
|
|
81
81
|
|
|
82
82
|
if not task_label:
|
|
83
83
|
error_notice("Please Enter a name before saving new task")
|
|
@@ -86,7 +86,10 @@ class NewTaskWDialog(Gtk.Dialog):
|
|
|
86
86
|
parent_list = self.selected_list
|
|
87
87
|
todolist_conf = conf.user['todolists'][parent_list['todolist']]
|
|
88
88
|
try:
|
|
89
|
+
|
|
90
|
+
conf.file_watch_ignores[todolist_conf['id']] = True
|
|
89
91
|
task = conf.todo_connectors[todolist_conf['type']].add_new_task(todolist_conf,parent_list,task_label)
|
|
92
|
+
del conf.file_watch_ignores[todolist_conf['id']]
|
|
90
93
|
|
|
91
94
|
dbg('connector add task response',task)
|
|
92
95
|
|