nowfocus 0.2.7__tar.gz → 0.2.12__tar.gz

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.
Files changed (64) hide show
  1. {nowfocus-0.2.7/src/nowfocus.egg-info → nowfocus-0.2.12}/PKG-INFO +9 -6
  2. {nowfocus-0.2.7 → nowfocus-0.2.12}/README.md +7 -5
  3. {nowfocus-0.2.7 → nowfocus-0.2.12}/pyproject.toml +3 -5
  4. nowfocus-0.2.12/src/nowfocus/__init__.py +1 -0
  5. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/__main__.py +49 -28
  6. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/conf.py +12 -5
  7. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/todotxt.py +2 -19
  8. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/txt.py +31 -20
  9. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/vikunja.py +6 -4
  10. nowfocus-0.2.12/src/nowfocus/icon/icon-red.svg +101 -0
  11. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon.svg +12 -1
  12. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/new_task_dialog.py +4 -1
  13. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/settings.py +55 -25
  14. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/task_window.py +6 -18
  15. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/utils.py +143 -21
  16. {nowfocus-0.2.7 → nowfocus-0.2.12/src/nowfocus.egg-info}/PKG-INFO +9 -6
  17. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus.egg-info/SOURCES.txt +1 -1
  18. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus.egg-info/requires.txt +1 -0
  19. nowfocus-0.2.7/src/nowfocus/__init__.py +0 -68
  20. nowfocus-0.2.7/src/nowfocus/desktop-extras/set_gsettings_keybinding.sh +0 -51
  21. {nowfocus-0.2.7 → nowfocus-0.2.12}/LICENSE +0 -0
  22. {nowfocus-0.2.7 → nowfocus-0.2.12}/setup.cfg +0 -0
  23. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/activitywatch.py +0 -0
  24. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/caldav.py +0 -0
  25. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/csv.py +0 -0
  26. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/psc_timetracker.py +0 -0
  27. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/taskwarrior.py +0 -0
  28. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/timewarrior.py +0 -0
  29. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/todo_template.py +0 -0
  30. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/connectors/trello.py +0 -0
  31. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/desktop-extras/nowfocus.desktop +0 -0
  32. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/desktop-extras/nowfocus.png +0 -0
  33. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/desktop-extras/nowfocus.svg +0 -0
  34. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/example-todo.txt +0 -0
  35. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/cancel.png +0 -0
  36. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/cancel.svg +0 -0
  37. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/edit.png +0 -0
  38. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/edit.svg +0 -0
  39. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-0.svg +0 -0
  40. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-1.svg +0 -0
  41. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-2.svg +0 -0
  42. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-3.svg +0 -0
  43. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-4.svg +0 -0
  44. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-5.svg +0 -0
  45. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-6.svg +0 -0
  46. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-7.svg +0 -0
  47. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-8.svg +0 -0
  48. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/icon-9.svg +0 -0
  49. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/mark-done.png +0 -0
  50. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/mark-done.svg +0 -0
  51. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/pause.png +0 -0
  52. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/pause.svg +0 -0
  53. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/settings.png +0 -0
  54. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/icon/settings.svg +0 -0
  55. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/session_edit_dialog.py +0 -0
  56. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/session_options.py +0 -0
  57. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/sessions.csv +0 -0
  58. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/sound/bell-xylophone-g.mp3 +0 -0
  59. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/sound/dinner-bell.mp3 +0 -0
  60. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/sound/xylophone-chord.mp3 +0 -0
  61. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus/styles.css +0 -0
  62. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus.egg-info/dependency_links.txt +0 -0
  63. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus.egg-info/entry_points.txt +0 -0
  64. {nowfocus-0.2.7 → nowfocus-0.2.12}/src/nowfocus.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nowfocus
3
- Version: 0.2.7
3
+ Version: 0.2.12
4
4
  Summary: nowfocus: the open source task-tracking self-control panel.
5
5
  Author: GitFr33
6
6
  Project-URL: Homepage, https://gitlab.com/GitFr33/nowfocus
@@ -42,6 +42,7 @@ Requires-Dist: tzlocal
42
42
  Requires-Dist: urllib3
43
43
  Requires-Dist: vobject
44
44
  Requires-Dist: x-wr-timezone
45
+ Requires-Dist: watchdog
45
46
  Dynamic: license-file
46
47
 
47
48
  <div align="center"><img src="https://gitlab.com/GitFr33/nowfocus/-/raw/main/nowfocus.svg" width="60" align="center">
@@ -52,15 +53,15 @@ Dynamic: license-file
52
53
 
53
54
  </div>
54
55
 
55
- nowfocus is a clean, keyboard-driven project time tracker build with python + GTK that flexibly connects multiple to-do lists with multiple time trackers and displays your current task and time spent in the status bar.
56
+ nowfocus is a clean, keyboard-driven project time tracker built with python + GTK that flexibly connects multiple to-do lists with multiple time trackers and displays your current task and time spent in the status bar.
56
57
 
57
58
  ## Features
58
59
  - Unlimited flexible combinations of to-do lists and time tracking systems
59
60
  - Infinitely nestable lists
60
61
  - Inactivity detection that automatically pauses time tracking
61
62
  - Pomodoro timer
62
- - Task prioritization
63
- - Time targets: set a minimum or maximum time for any task or list of tasks and get reminded to follow though
63
+ - Task prioritization
64
+ - Time targets: set a minimum or maximum time for any task or list of tasks and get reminded to follow though
64
65
  - Randomness interrupt bell (optional) to keep you on track with tracking your time
65
66
  - Keyboard-driven interface
66
67
  - Offline to-do list cache
@@ -76,7 +77,7 @@ nowfocus is a clean, keyboard-driven project time tracker build with python + GT
76
77
 
77
78
  ### Currently Supported To-do List Backends
78
79
 
79
- - Simple text or markdown file with indentation based sub-lists
80
+ - Simple text file with indentation based sub-lists
80
81
  - Any to-do list that supports [CalDav todos](https://en.wikipedia.org/wiki/CalDAV)
81
82
  - [todotxt format](http://todotxt.org/)
82
83
  - [TaskWarrior](https://taskwarrior.org/)
@@ -99,7 +100,9 @@ nowfocus is a clean, keyboard-driven project time tracker build with python + GT
99
100
  # Install dependencies
100
101
  sudo apt install pipx gir1.2-appindicator3-0.1 meson libdbus-glib-1-dev patchelf libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev
101
102
 
102
- # Set up pipx
103
+ # note gir1.2-ayatanaappindicator3-0.1 can be substituted for gir1.2-appindicator3-0.1
104
+
105
+ # Set up pipx
103
106
  pipx ensurepath
104
107
 
105
108
  # At this point you may need to restart your terminal window
@@ -6,15 +6,15 @@
6
6
 
7
7
  </div>
8
8
 
9
- nowfocus is a clean, keyboard-driven project time tracker build with python + GTK that flexibly connects multiple to-do lists with multiple time trackers and displays your current task and time spent in the status bar.
9
+ nowfocus is a clean, keyboard-driven project time tracker built with python + GTK that flexibly connects multiple to-do lists with multiple time trackers and displays your current task and time spent in the status bar.
10
10
 
11
11
  ## Features
12
12
  - Unlimited flexible combinations of to-do lists and time tracking systems
13
13
  - Infinitely nestable lists
14
14
  - Inactivity detection that automatically pauses time tracking
15
15
  - Pomodoro timer
16
- - Task prioritization
17
- - Time targets: set a minimum or maximum time for any task or list of tasks and get reminded to follow though
16
+ - Task prioritization
17
+ - Time targets: set a minimum or maximum time for any task or list of tasks and get reminded to follow though
18
18
  - Randomness interrupt bell (optional) to keep you on track with tracking your time
19
19
  - Keyboard-driven interface
20
20
  - Offline to-do list cache
@@ -30,7 +30,7 @@ nowfocus is a clean, keyboard-driven project time tracker build with python + GT
30
30
 
31
31
  ### Currently Supported To-do List Backends
32
32
 
33
- - Simple text or markdown file with indentation based sub-lists
33
+ - Simple text file with indentation based sub-lists
34
34
  - Any to-do list that supports [CalDav todos](https://en.wikipedia.org/wiki/CalDAV)
35
35
  - [todotxt format](http://todotxt.org/)
36
36
  - [TaskWarrior](https://taskwarrior.org/)
@@ -53,7 +53,9 @@ nowfocus is a clean, keyboard-driven project time tracker build with python + GT
53
53
  # Install dependencies
54
54
  sudo apt install pipx gir1.2-appindicator3-0.1 meson libdbus-glib-1-dev patchelf libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev
55
55
 
56
- # Set up pipx
56
+ # note gir1.2-ayatanaappindicator3-0.1 can be substituted for gir1.2-appindicator3-0.1
57
+
58
+ # Set up pipx
57
59
  pipx ensurepath
58
60
 
59
61
  # At this point you may need to restart your terminal window
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nowfocus"
3
- version = "0.2.7"
3
+ version = "0.2.12"
4
4
  authors = [
5
5
  { name="GitFr33" },
6
6
  ]
@@ -15,12 +15,9 @@ classifiers = [
15
15
  "Intended Audience :: End Users/Desktop",
16
16
  ]
17
17
 
18
- # license-files = ["LICEN[CS]E*"]
18
+ license-files = ["LICENSE"]
19
19
 
20
20
  dependencies = [
21
- # "pycairo",
22
- # "PyGObject",
23
-
24
21
  "psutil",
25
22
  "pygobject == 3.50.0",
26
23
  "pycairo == 1.27",
@@ -52,6 +49,7 @@ dependencies = [
52
49
  "urllib3",
53
50
  "vobject",
54
51
  "x-wr-timezone",
52
+ "watchdog",
55
53
  ]
56
54
 
57
55
  [project.urls]
@@ -0,0 +1 @@
1
+
@@ -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
 
@@ -73,7 +72,7 @@ class Application(Gtk.Application):
73
72
  self.menu_tasks = {}
74
73
  self.list_menus = {}
75
74
 
76
- self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-0.svg'), appindicator.IndicatorCategory.APPLICATION_STATUS)
75
+ self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-1.svg'), appindicator.IndicatorCategory.APPLICATION_STATUS)
77
76
 
78
77
  self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
79
78
 
@@ -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(pipe_file)")
167
- os.remove(pipe_file)
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 pipe_file in quit",e)
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):
@@ -301,7 +304,7 @@ class Application(Gtk.Application):
301
304
  if(self.is_running == True):
302
305
  self.icon_tick_number = self.icon_tick_number + 1
303
306
 
304
- if self.icon_tick_number > 9:
307
+ if self.icon_tick_number > 8:
305
308
  self.icon_tick_number = 1
306
309
 
307
310
  label = self.session['label'] + ": " + sec_to_time(self.session['duration'])
@@ -312,12 +315,21 @@ class Application(Gtk.Application):
312
315
 
313
316
  # label = random.choice(conf.idle_messages) # Cool but makes menu bounce around #Could be paused when the menu opens
314
317
  label = conf.user['default_text']
315
-
316
- indicator.set_icon_full(os.path.abspath('icon/icon-0.svg'),label)
318
+ if self.session['duration'] > 60 and self.session['duration'] % 2:
319
+ indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
320
+ else:
321
+ indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
317
322
 
318
323
  # https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
319
324
  indicator.set_label(label, "Wide")
320
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 = {}
321
333
  return True
322
334
 
323
335
 
@@ -414,6 +426,7 @@ class Application(Gtk.Application):
414
426
 
415
427
  if process:
416
428
  print("waiting for already running command with psutils", command)
429
+ # subprocess.run('wmctrl', '-a', command) # Doesn't work on wayland
417
430
  process.wait()
418
431
  else:
419
432
  print("Launching command with subprocess.run", command)
@@ -428,13 +441,23 @@ class Application(Gtk.Application):
428
441
 
429
442
  def mark_done(self, w=None, task = None):
430
443
  ''' second (task) argument is required and must be a task object '''
444
+
431
445
  print("Mark Task done")
432
446
  print(task)
433
447
 
434
448
  todolist_conf = conf.user['todolists'][task['todolist']]
435
449
 
436
450
  try:
451
+
437
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
+ #
438
461
  done_thread.start()
439
462
 
440
463
  db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
@@ -451,7 +474,7 @@ class Application(Gtk.Application):
451
474
 
452
475
 
453
476
  def stop_task(self, w = '', action = 'save', custom_end_time=None):
454
- ''' supported actions are save,cancel,mark_done '''
477
+ ''' supported actions are save, cancel, mark_done '''
455
478
 
456
479
  list_menus = self.list_menus
457
480
  menu_tasks = self.menu_tasks
@@ -540,7 +563,6 @@ class Application(Gtk.Application):
540
563
  i = Gtk.ImageMenuItem.new_with_label("Edit Session")
541
564
  i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/edit.svg')))
542
565
 
543
- # i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/status/software-installed-symbolic.svg'))
544
566
  i.set_always_show_image(True)
545
567
  i.connect("activate", self.open_session_options_dialog,'from_menu')
546
568
  self.menu.insert(i,0)
@@ -548,33 +570,34 @@ class Application(Gtk.Application):
548
570
  i = Gtk.ImageMenuItem.new_with_label("Pause" )
549
571
  i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/pause.svg')))
550
572
 
551
- # i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/multimedia/pause-symbolic.svg'))
552
573
  i.set_always_show_image(True)
553
574
  i.connect("activate", self.stop_task)
554
575
  self.menu.insert(i,0)
555
576
 
556
577
 
557
- def async_refresh(self, w=None):
578
+ def async_refresh(self, w=None, single_todo = None):
558
579
 
559
580
  self.indicator.set_label("Refreshing Todolists", "Wide")
560
581
  menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
561
582
  self.menu.append(menu_item)
562
583
  self.menu.show_all()
563
584
 
564
- connectors_thread = threading.Thread(target=self.async_refresh_inner)
585
+ connectors_thread = threading.Thread(target=self.async_refresh_inner,args=(single_todo,))
565
586
  connectors_thread.start()
566
587
 
567
588
 
568
- def async_refresh_inner(self):
589
+ def async_refresh_inner(self, single_todo = None):
569
590
  # dbg("async refresh started",s="todoloading",l=3)
570
- utils.get_todolists()
591
+ if single_todo:
592
+ utils.refresh_todolist(single_todo)
593
+ utils.reindex()
594
+
595
+ else:
596
+ utils.get_todolists()
597
+
571
598
  dbg("async refresh complete",s="todoloading",l=3)
572
599
  GLib.idle_add(self.update_menu)
573
600
 
574
- try:
575
- GLib.idle_add(self.taskwindow.refresh_search_cache)
576
- except Exception:
577
- pass
578
601
 
579
602
 
580
603
  def update_menu(self, w = ''):
@@ -724,10 +747,10 @@ class Application(Gtk.Application):
724
747
  dbg("no handler for received signal",s='signals',l=3)
725
748
 
726
749
  def check_pipe(self):
727
- # print("Listening to pipe at ",pipe_file)
750
+ # print("Listening to pipe at ",conf.pipe)
728
751
 
729
752
  try:
730
- with open(pipe_file, "r") as pipe:
753
+ with open(conf.pipe, "r") as pipe:
731
754
  data = pipe.read().strip()
732
755
 
733
756
  print("check_pipe ")
@@ -736,10 +759,8 @@ class Application(Gtk.Application):
736
759
  pipe.close()
737
760
 
738
761
  # TODO: add registry of special commands
762
+ # How to handle function arguments? (for example refresh a todolist with it's id)
739
763
  # if data in ['quit','open_task_window']:
740
- # TODO: how to all dynamic function name here?
741
- # GLib.idle_add(self.open_task_window)
742
-
743
764
 
744
765
  if data == 'quit':
745
766
  GLib.idle_add(self.quit)
@@ -792,13 +813,13 @@ def startup():
792
813
  if args.force:
793
814
  print("Lanched with --force flag, forcibly deleting old pipe")
794
815
  try:
795
- os.remove(pipe_file)
816
+ os.remove(conf.pipe)
796
817
  except Exception as e:
797
818
  print(e)
798
819
 
799
820
 
800
821
  try:
801
- os.mkfifo(pipe_file)
822
+ os.mkfifo(conf.pipe)
802
823
  dbg("Named pipe created successfully!", s="cli")
803
824
 
804
825
  signal.signal(signal.SIGUSR1, Application.signal_handler)
@@ -806,7 +827,7 @@ def startup():
806
827
 
807
828
  if args.task:
808
829
  print("Writing args.task to pipe", args.task)
809
- with open(pipe_file, "w") as pipeout:
830
+ with open(conf.pipe, "w") as pipeout:
810
831
  pipeout.write(args.task)
811
832
  pipeout.close()
812
833
 
@@ -824,7 +845,7 @@ def startup():
824
845
 
825
846
  dbg("Writing arg to pipe ",pipe_line, s="cli")
826
847
 
827
- with open(pipe_file, "w") as pipeout:
848
+ with open(conf.pipe, "w") as pipeout:
828
849
  pipeout.write(pipe_line)
829
850
  pipeout.close()
830
851
 
@@ -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
- 'todotxt':{'id':'todotxt','type':'todotxt','label':'','file':"","status":True,'timetracker':''},
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": 26,
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":6,
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':18,
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,
@@ -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
 
@@ -11,24 +11,32 @@ def add_new_task(user_conf,list,task_label):
11
11
  # read a list of lines into data
12
12
  data = file.readlines()
13
13
 
14
- if list['id'] == user_conf['id']:
15
- # Top level insert
16
- new_line = task_label+"\n"
17
- line_no = len(data) + 1
18
- else:
19
- # Sub list insert
20
- # TODO: Add it at the end of the list rather than the start ...
21
- # Yup. rjust(indent + len)
22
-
23
- 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
14
+ # New Version
15
+
16
+ line_no = get_line_no(list,data) + 1
17
+ new_line = list['data']['indent']+task_label+"\n"
18
+
19
+ # # Old version without recorded indent
20
+
21
+ # if list['id'] == user_conf['id']:
22
+ # # Top level insert
23
+ # new_line = task_label+"\n"
24
+ # line_no = len(data) + 1
25
+ # else:
26
+ # Sub list insert
27
+ # TODO: Add it at the end of the list rather than the start ...
28
+ # Yup. rjust(indent + len)
29
+
30
+
31
+ # 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
32
 
25
- new_line = task_label+"\n"
26
- # I'm sure there's a better way to add a bunch of spaces to to a string
27
- while indent > 0:
28
- new_line = " " + new_line
29
- indent = indent - 1
33
+ # new_line = task_label+"\n"
34
+ # # I'm sure there's a better way to add a bunch of spaces to to a string
35
+ # while indent > 0:
36
+ # new_line = " " + new_line
37
+ # indent = indent - 1
30
38
 
31
- line_no = get_line_no(list,data) + 1
39
+ # line_no = get_line_no(list,data) + 1
32
40
 
33
41
  data.insert(line_no,new_line)
34
42
 
@@ -128,7 +136,8 @@ def get_todos(user_conf):
128
136
  'data':{
129
137
  'line_no':0,
130
138
  'original_line':'',
131
- 'accepts_tasks':True
139
+ 'accepts_tasks':True,
140
+ 'indent':'',
132
141
  }
133
142
  }
134
143
 
@@ -149,7 +158,7 @@ def get_todos(user_conf):
149
158
  # file_uri = "todo.txt"
150
159
  line_no = -1
151
160
 
152
- with open(file_uri) as file:
161
+ with open(file_uri,"r") as file:
153
162
  for line in file:
154
163
 
155
164
  line_no = line_no + 1
@@ -171,11 +180,13 @@ def get_todos(user_conf):
171
180
  #TODO: use markdown title syntax as list name in addition to indentation
172
181
 
173
182
  indent = len(line) - len(line.lstrip())
183
+ indent_str = line[0:indent]
174
184
 
175
185
  if indent > prev_indent:
176
- # prev item is a sub_list header, move it to lists
186
+ # prev item is a sub_list header, copy it to lists
177
187
  lists[prev_id] = tasks[prev_id]
178
- del tasks[prev_id]
188
+ lists[prev_id]['data']['indent'] = indent_str
189
+ # del tasks[prev_id]
179
190
  current_list.append(prev_id)
180
191
  current_list_label.append(prev_label)
181
192
  current_indent.append(indent)
@@ -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 = None):
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 and category:
302
- if category == 'list':
303
+ if item:
304
+ if category in ('list','lists'):
303
305
  url += 'projects/'+str(item['data']['id'])
304
- elif category == 'task':
306
+ else:
305
307
  url += 'tasks/'+str(item['data']['id'])
306
308
 
307
309
  utils.open_external(url)
@@ -0,0 +1,101 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ width="48"
6
+ height="48"
7
+ viewBox="0 0 12.7 12.7"
8
+ version="1.1"
9
+ id="svg1"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ xmlns:svg="http://www.w3.org/2000/svg">
12
+ <defs
13
+ id="defs1" />
14
+ <g
15
+ id="layer1"
16
+ transform="matrix(1.0567177,0,0,1.0567177,-0.36015771,-0.36015771)">
17
+ <rect
18
+ style="display:none;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.316;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
19
+ id="rect1"
20
+ width="9.5708742"
21
+ height="9.5708742"
22
+ x="1.5645629"
23
+ y="1.5645629" />
24
+ <rect
25
+ style="display:none;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
26
+ id="rect38"
27
+ width="9.5708742"
28
+ height="9.5708742"
29
+ x="1.5645629"
30
+ y="1.5645629"
31
+ ry="3.110863" />
32
+ <rect
33
+ style="display:inline;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
34
+ id="rect2"
35
+ width="9.5708742"
36
+ height="9.5708742"
37
+ x="1.5645629"
38
+ y="1.5645629"
39
+ ry="4.7854371"
40
+ rx="4.7854371" />
41
+ </g>
42
+ <g
43
+ id="layer2"
44
+ style="display:inline">
45
+ <path
46
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
47
+ d="M 6.3410664,6.36546 4.4335013,4.0522236"
48
+ id="path2" />
49
+ <path
50
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
51
+ d="M 6.37713,6.3615649 11.260009,1.2965066"
52
+ id="use31" />
53
+ <path
54
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
55
+ d="M 6.3610066,6.3773634 13.39526,6.248543"
56
+ id="use32" />
57
+ <path
58
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
59
+ d="m 6.338434,6.3771308 5.065058,4.8828782"
60
+ id="use33" />
61
+ <path
62
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
63
+ d="M 6.322637,6.3610073 6.4514574,13.395261"
64
+ id="use34" />
65
+ <path
66
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
67
+ d="M 6.322869,6.3384356 1.4399903,11.403494"
68
+ id="use35" />
69
+ <path
70
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
71
+ d="M 6.3389932,6.3226376 -0.69526049,6.451458"
72
+ id="use36" />
73
+ <path
74
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
75
+ d="M 6.3615647,6.3228685 1.2965064,1.4399898"
76
+ id="use37" />
77
+ <path
78
+ style="display:none;fill:none;stroke:#ffffff;stroke-width:1.465;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
79
+ d="M 6.3773615,6.3389927 6.2485411,-0.69526102"
80
+ id="use38" />
81
+ </g>
82
+ <g
83
+ id="layer3"
84
+ style="display:inline">
85
+ <rect
86
+ style="display:inline;opacity:1;fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.915798;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
87
+ id="rect3"
88
+ width="9.5708742"
89
+ height="9.5708742"
90
+ x="1.5645629"
91
+ y="1.5645629"
92
+ ry="4.7854371"
93
+ rx="4.7854371"
94
+ transform="matrix(1.0567177,0,0,1.0567177,-0.36015771,-0.36015771)" />
95
+ <path
96
+ style="font-weight:bold;font-size:10.4396px;line-height:1.25;font-family:'Besley*';-inkscape-font-specification:'Besley*, Bold';letter-spacing:0px;word-spacing:0px;fill:#ffffff;stroke-width:0.198419"
97
+ d="m 5.2723149,3.6126858 c 0,-0.040213 0.03016,-0.080427 0.1005332,-0.1407465 0.06032,-0.06032 0.1709064,-0.1105865 0.3116529,-0.1507998 0.1407465,-0.040213 0.2915463,-0.070373 0.4624528,-0.070373 0.4121862,0 0.6936791,0.1105865 0.8545323,0.3317595 0.1507998,0.2211731 0.2312264,0.5428794 0.2312264,0.9550655 0,0.4222395 -0.1206399,0.7640524 -0.3518663,1.0254388 -0.2412797,0.2613863 -0.5830926,0.4825594 -1.0254387,0.6434125 -0.03016,0.020106 -0.080427,0.040213 -0.1608531,0.06032 -0.080427,0.020106 -0.1306932,0.040213 -0.1407465,0.050267 -0.020106,0.010054 -0.040213,0.03016 -0.080427,0.070373 -0.040213,0.040213 -0.06032,0.080427 -0.06032,0.1407464 -0.010054,0.06032 -0.010054,0.1306932 -0.010054,0.2211731 v 0.8243723 c 0,0.080427 0.010054,0.1407465 0.050267,0.1608532 0.03016,0.020106 0.1005332,0.03016 0.2211731,0.03016 h 0.9852264 c 0.1608531,0 0.2613863,-0.010054 0.3015996,-0.040213 C 6.991433,7.6943348 7.01154,7.603855 7.01154,7.4430018 V 6.9906026 c 0,-0.070373 0,-0.1306932 0.010054,-0.1809598 0,-0.040213 0.010054,-0.080427 0.020106,-0.1105865 0.010054,-0.020106 0.03016,-0.050267 0.06032,-0.070373 0.03016,-0.020106 0.050267,-0.03016 0.070373,-0.040213 0.020106,0 0.050267,-0.010054 0.1105865,-0.03016 0.06032,-0.010054 0.1005332,-0.020106 0.1407465,-0.040213 C 7.9263916,6.3572424 8.3184711,6.1159627 8.6100174,5.7842031 8.9015637,5.4624969 9.0523635,5.0402574 9.0523635,4.5375913 c 0,-0.6233059 -0.221173,-1.1159186 -0.6635191,-1.4778381 -0.4423462,-0.3518663 -1.1159187,-0.532826 -2.0408242,-0.532826 -0.8042657,0 -1.4376249,0.2111197 -1.8900243,0.6132525 -0.4624528,0.4121862 -0.6836258,0.8746389 -0.6836258,1.3873583 0,0.3418129 0.090479,0.6132526 0.2915463,0.814319 0.2010664,0.2010664 0.4423461,0.3015996 0.7338924,0.3015996 0.2714397,0 0.502666,-0.080427 0.6936791,-0.251333 0.1809598,-0.1709064 0.281493,-0.3920795 0.281493,-0.6534658 0,-0.2010665 -0.050267,-0.3820262 -0.1306931,-0.5428794 C 5.5638612,4.0349253 5.4733813,3.9142854 5.3929547,3.8238056 5.3125282,3.7333257 5.2723149,3.6629524 5.2723149,3.6126858 Z m -0.050267,5.8510328 c 0,0.2814929 0.090479,0.5127198 0.2915463,0.7137864 0.2010664,0.201066 0.4322928,0.291546 0.7137858,0.291546 0.2714396,0 0.502666,-0.09048 0.7037324,-0.291546 C 7.1321792,9.9764384 7.2327125,9.7452115 7.2327125,9.4637186 7.2327125,9.1922789 7.1321792,8.9610525 6.9311128,8.7599861 6.7300464,8.5589197 6.49882,8.4583865 6.2273804,8.4583865 c -0.281493,0 -0.5127194,0.1005332 -0.7137858,0.3015996 C 5.3125282,8.9610525 5.2220483,9.1922789 5.2220479,9.4637186 Z"
98
+ id="text38"
99
+ aria-label="?" />
100
+ </g>
101
+ </svg>