nowfocus 0.2.13__tar.gz → 0.4.0__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 (63) hide show
  1. {nowfocus-0.2.13/src/nowfocus.egg-info → nowfocus-0.4.0}/PKG-INFO +17 -16
  2. {nowfocus-0.2.13 → nowfocus-0.4.0}/README.md +13 -7
  3. {nowfocus-0.2.13 → nowfocus-0.4.0}/pyproject.toml +12 -9
  4. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/__main__.py +80 -77
  5. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/conf.py +4 -5
  6. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/todotxt.py +8 -3
  7. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/txt.py +14 -2
  8. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/new_task_dialog.py +3 -4
  9. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/settings.py +10 -51
  10. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/styles.css +9 -11
  11. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/task_window.py +106 -67
  12. nowfocus-0.4.0/src/nowfocus/user_idle_time.py +82 -0
  13. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/utils.py +165 -107
  14. {nowfocus-0.2.13 → nowfocus-0.4.0/src/nowfocus.egg-info}/PKG-INFO +17 -16
  15. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus.egg-info/SOURCES.txt +1 -0
  16. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus.egg-info/requires.txt +3 -6
  17. {nowfocus-0.2.13 → nowfocus-0.4.0}/LICENSE +0 -0
  18. {nowfocus-0.2.13 → nowfocus-0.4.0}/setup.cfg +0 -0
  19. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/__init__.py +0 -0
  20. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/activitywatch.py +0 -0
  21. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/caldav.py +0 -0
  22. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/csv.py +0 -0
  23. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/psc_timetracker.py +0 -0
  24. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/taskwarrior.py +0 -0
  25. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/timewarrior.py +0 -0
  26. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/todo_template.py +0 -0
  27. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/trello.py +0 -0
  28. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/connectors/vikunja.py +0 -0
  29. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/desktop-extras/nowfocus.desktop +0 -0
  30. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/desktop-extras/nowfocus.png +0 -0
  31. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/desktop-extras/nowfocus.svg +0 -0
  32. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/example-todo.txt +0 -0
  33. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/cancel.png +0 -0
  34. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/cancel.svg +0 -0
  35. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/edit.png +0 -0
  36. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/edit.svg +0 -0
  37. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-0.svg +0 -0
  38. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-1.svg +0 -0
  39. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-2.svg +0 -0
  40. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-3.svg +0 -0
  41. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-4.svg +0 -0
  42. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-5.svg +0 -0
  43. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-6.svg +0 -0
  44. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-7.svg +0 -0
  45. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-8.svg +0 -0
  46. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-9.svg +0 -0
  47. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon-red.svg +0 -0
  48. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/icon.svg +0 -0
  49. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/mark-done.png +0 -0
  50. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/mark-done.svg +0 -0
  51. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/pause.png +0 -0
  52. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/pause.svg +0 -0
  53. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/settings.png +0 -0
  54. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/icon/settings.svg +0 -0
  55. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/session_edit_dialog.py +0 -0
  56. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/session_options.py +0 -0
  57. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/sessions.csv +0 -0
  58. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/sound/bell-xylophone-g.mp3 +0 -0
  59. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/sound/dinner-bell.mp3 +0 -0
  60. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus/sound/xylophone-chord.mp3 +0 -0
  61. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus.egg-info/dependency_links.txt +0 -0
  62. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus.egg-info/entry_points.txt +0 -0
  63. {nowfocus-0.2.13 → nowfocus-0.4.0}/src/nowfocus.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nowfocus
3
- Version: 0.2.13
3
+ Version: 0.4.0
4
4
  Summary: nowfocus: the open source task-tracking self-control panel.
5
- Author: GitFr33
6
- Project-URL: Homepage, https://gitlab.com/GitFr33/nowfocus
5
+ Author: AltruistEnterprises
6
+ Project-URL: Homepage, https://www.nowfocus.org
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: Operating System :: OS Independent
9
9
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
@@ -14,17 +14,13 @@ License-File: LICENSE
14
14
  Requires-Dist: psutil
15
15
  Requires-Dist: pygobject==3.50.0
16
16
  Requires-Dist: pycairo==1.27
17
- Requires-Dist: meson
18
- Requires-Dist: meson-python
19
17
  Requires-Dist: attrs
20
18
  Requires-Dist: caldav
21
19
  Requires-Dist: certifi
22
20
  Requires-Dist: charset-normalizer
23
- Requires-Dist: dbus-idle
24
- Requires-Dist: dbus-python
25
21
  Requires-Dist: icalendar
26
22
  Requires-Dist: idna
27
- Requires-Dist: jeepney
23
+ Requires-Dist: pywin32>=221; platform_system == "Windows"
28
24
  Requires-Dist: kitchen
29
25
  Requires-Dist: lxml
30
26
  Requires-Dist: playsound3
@@ -42,9 +38,11 @@ Requires-Dist: tzlocal
42
38
  Requires-Dist: urllib3
43
39
  Requires-Dist: vobject
44
40
  Requires-Dist: x-wr-timezone
45
- Requires-Dist: watchdog
46
41
  Dynamic: license-file
47
42
 
43
+ ---
44
+ mainfont: sans-serif
45
+ ---
48
46
  <div align="center"><img src="https://gitlab.com/GitFr33/nowfocus/-/raw/main/nowfocus.svg" width="60" align="center">
49
47
 
50
48
  # *nowfocus* <br> Open-source task timer for Linux
@@ -102,17 +100,20 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
102
100
 
103
101
  ## Installation
104
102
 
103
+ <!-- no longer needed: meson libdbus-glib-1-dev patchelf -->
104
+ <!-- # note: gir1.2-appindicator3-0.1 can be substituted for gir1.2-ayatanaappindicator3-0.1 -->
105
+
105
106
  1. Run the following in terminal to install and setup:
106
107
  ```
107
108
  # Install dependencies
108
- 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
109
+ sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev
109
110
 
110
- # note gir1.2-ayatanaappindicator3-0.1 can be substituted for gir1.2-appindicator3-0.1
111
+ # only needed X display (rather than wayland)
112
+ sudo apt install xprintidle
111
113
 
112
114
  # Set up pipx
113
115
  pipx ensurepath
114
-
115
- # At this point you may need to source your .bashrc (or open a new terminal)
116
+ source ~.bashrc
116
117
 
117
118
  # Install nowfocus
118
119
  pipx install nowfocus
@@ -173,15 +174,15 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
173
174
  - Start with verbose logging use: `nowfocus -l 3`
174
175
  - Start with targeted verbose logging use: `nowfocus -s trello`
175
176
 
176
-
177
+ <!--
178
+ TODO: test that this works!
177
179
  ## Development
178
180
 
179
181
  ### Install from Source
180
182
 
181
- - Install dependencies from above.
182
183
  - Clone this repo somewhere (referred to as `YOUR_INSTALL_PATH`)
183
184
  - Change to `YOUR_INSTALL_PATH` directory with `cd YOUR_INSTALL_PATH/nowfocus`
184
185
  - build python module with `python3 -m build` (this should be done in a venv and will require some dependecies...)
185
- - pipx install -e --force YOUR_INSTALL_PATH/monotask/
186
+ - pipx install -e --force YOUR_INSTALL_PATH/monotask/ -->
186
187
 
187
188
  <!--built with python + GTK -->
@@ -1,3 +1,6 @@
1
+ ---
2
+ mainfont: sans-serif
3
+ ---
1
4
  <div align="center"><img src="https://gitlab.com/GitFr33/nowfocus/-/raw/main/nowfocus.svg" width="60" align="center">
2
5
 
3
6
  # *nowfocus* <br> Open-source task timer for Linux
@@ -55,17 +58,20 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
55
58
 
56
59
  ## Installation
57
60
 
61
+ <!-- no longer needed: meson libdbus-glib-1-dev patchelf -->
62
+ <!-- # note: gir1.2-appindicator3-0.1 can be substituted for gir1.2-ayatanaappindicator3-0.1 -->
63
+
58
64
  1. Run the following in terminal to install and setup:
59
65
  ```
60
66
  # Install dependencies
61
- 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
67
+ sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev
62
68
 
63
- # note gir1.2-ayatanaappindicator3-0.1 can be substituted for gir1.2-appindicator3-0.1
69
+ # only needed X display (rather than wayland)
70
+ sudo apt install xprintidle
64
71
 
65
72
  # Set up pipx
66
73
  pipx ensurepath
67
-
68
- # At this point you may need to source your .bashrc (or open a new terminal)
74
+ source ~.bashrc
69
75
 
70
76
  # Install nowfocus
71
77
  pipx install nowfocus
@@ -126,15 +132,15 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
126
132
  - Start with verbose logging use: `nowfocus -l 3`
127
133
  - Start with targeted verbose logging use: `nowfocus -s trello`
128
134
 
129
-
135
+ <!--
136
+ TODO: test that this works!
130
137
  ## Development
131
138
 
132
139
  ### Install from Source
133
140
 
134
- - Install dependencies from above.
135
141
  - Clone this repo somewhere (referred to as `YOUR_INSTALL_PATH`)
136
142
  - Change to `YOUR_INSTALL_PATH` directory with `cd YOUR_INSTALL_PATH/nowfocus`
137
143
  - build python module with `python3 -m build` (this should be done in a venv and will require some dependecies...)
138
- - pipx install -e --force YOUR_INSTALL_PATH/monotask/
144
+ - pipx install -e --force YOUR_INSTALL_PATH/monotask/ -->
139
145
 
140
146
  <!--built with python + GTK -->
@@ -1,10 +1,11 @@
1
1
  [project]
2
2
  name = "nowfocus"
3
- version = "0.2.13"
3
+ version = "0.4.0"
4
4
  authors = [
5
- { name="GitFr33" },
5
+ { name="AltruistEnterprises" },
6
6
  ]
7
7
  description = "nowfocus: the open source task-tracking self-control panel."
8
+
8
9
  readme = "README.md"
9
10
 
10
11
  classifiers = [
@@ -21,17 +22,18 @@ dependencies = [
21
22
  "psutil",
22
23
  "pygobject == 3.50.0",
23
24
  "pycairo == 1.27",
24
- "meson",
25
- "meson-python",
25
+ # "meson",
26
+ # "meson-python",
26
27
  "attrs",
27
28
  "caldav",
28
29
  "certifi",
29
30
  "charset-normalizer",
30
- "dbus-idle",
31
- "dbus-python",
31
+ # "dbus-idle",
32
+ # "dbus-python",
32
33
  "icalendar",
33
34
  "idna",
34
- "jeepney",
35
+ # "jeepney",
36
+ "pywin32>=221 ; platform_system == 'Windows'",
35
37
  "kitchen",
36
38
  "lxml",
37
39
  "playsound3",
@@ -49,11 +51,12 @@ dependencies = [
49
51
  "urllib3",
50
52
  "vobject",
51
53
  "x-wr-timezone",
52
- "watchdog",
53
54
  ]
54
55
 
55
56
  [project.urls]
56
- Homepage = "https://gitlab.com/GitFr33/nowfocus"
57
+ Homepage = "https://www.nowfocus.org"
58
+ # Homepage = "https://codeberg.org/AltruistEnterprises/nowfocus"
59
+ # Homepage = "https://gitlab.com/GitFr33/nowfocus"
57
60
 
58
61
  [project.gui-scripts]
59
62
  nowfocus = "nowfocus.__main__:startup"
@@ -13,6 +13,7 @@ from playsound3 import playsound
13
13
  import setproctitle
14
14
  import psutil
15
15
  import argparse
16
+ import traceback
16
17
 
17
18
  import gi
18
19
  gi.require_version('Gtk', '3.0')
@@ -28,7 +29,7 @@ except Exception as e:
28
29
  gi.require_version('AppIndicator3', '0.1')
29
30
  from gi.repository import AppIndicator3 as appindicator
30
31
 
31
- from dbus_idle import IdleMonitor
32
+ # from dbus_idle import IdleMonitor
32
33
 
33
34
  # Set working dir to file location
34
35
  os.chdir(os.path.dirname(os.path.realpath(__file__)))
@@ -39,6 +40,8 @@ sys.path.append(os.path.dirname(__file__))
39
40
  # from . import conf # this works in module context but not running as pile-of-files
40
41
  import conf # this works running as pile-of-files but not in module context without sys.path.append
41
42
 
43
+ from user_idle_time import UserIdleTime
44
+
42
45
  import utils
43
46
  from utils import *
44
47
 
@@ -55,19 +58,21 @@ print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file_
55
58
 
56
59
  class Application(Gtk.Application):
57
60
  def __init__(self, *args, **kwargs):
58
- super().__init__(*args, application_id="org.example.myapp", **kwargs)
61
+ super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
62
+
63
+ # try:
64
+ # # To put everything here...
65
+ # # this doesn't work because as soon as an exception occurs ii jumps to the handler and breaks all the following code.
59
66
 
67
+ # except Exception as e:
68
+ # print(conf.app_name +' had a pretty bad error. Please submit the following trace as an issue on the git repo or email it to the developers ')
69
+ # traceback.print_tb(e.__traceback__)
70
+ # return None
71
+
60
72
  self.window = None
61
73
 
62
74
  self.is_running = False
63
- self.session = {
64
- "label":"Randomness",
65
- "extended_label": 'Randomness',
66
- "start_time":datetime.now(),
67
- "duration":0,
68
- 'task':{},
69
- 'notes':'',
70
- }
75
+ self.session = default_session()
71
76
 
72
77
  self.menu_tasks = {}
73
78
  self.list_menus = {}
@@ -80,7 +85,7 @@ class Application(Gtk.Application):
80
85
  # menu.set_reserve_toggle_size(False) # skip menu left padding, doesn't work
81
86
 
82
87
  utils.db_init()
83
- utils.db_update()
88
+ utils.db_schema_update()
84
89
 
85
90
 
86
91
  # self.update_menu()
@@ -90,7 +95,6 @@ class Application(Gtk.Application):
90
95
 
91
96
  self.indicator.set_menu(self.menu)
92
97
 
93
- # main_tick_timer = GLib.timeout_add_seconds(conf.user['tick_interval'], self.tick)
94
98
  main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
95
99
 
96
100
  try:
@@ -105,7 +109,7 @@ class Application(Gtk.Application):
105
109
  except Exception as e:
106
110
  dbg("Error resuming session",e,l=1)
107
111
 
108
- utils.start_todo_file_watchers()
112
+ self.UserIdleTime = UserIdleTime()
109
113
 
110
114
  self.pipethread = threading.Thread(target=self.check_pipe)
111
115
  self.pipethread.daemon = True
@@ -157,39 +161,40 @@ class Application(Gtk.Application):
157
161
 
158
162
  def quit(self, widget_or_signal_source=None, condition=None):
159
163
  print("Adios ", conf.app_name)
160
- # print("widget_or_signal_source ", widget_or_signal_source)
161
- # print("condition ", condition)
162
164
 
163
165
  if self.is_running:
164
- print('Caching active session', self.session['label'])
166
+ dbg('Caching active session', self.session['label'])
165
167
  db_set_session_cache(self.session)
168
+
166
169
  try:
167
- # print("before os.remove(conf.pipe)")
168
170
  os.remove(conf.pipe)
169
- print("Pipe removed")
170
171
  except Exception as e:
171
- print("Error removing conf.pipe in quit",e)
172
+ dbd("Error removing conf.pipe in quit",e)
172
173
 
173
174
  notify.uninit()
174
175
  Gtk.main_quit()
175
176
  exit()
176
177
 
177
178
 
178
- def check_afk_time(self):
179
+ def seconds_since_user_active(self):
179
180
  # returns seconds of inactivity
180
- # See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
181
+ # See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
181
182
 
182
183
  # Works on x11 but not wayland
183
184
  # Requires xprintidle (sudo apt install xprintidle)
184
185
  # idle_time = int(int(subprocess.getoutput('xprintidle')) / 1000)
185
186
 
186
- # Currently using: https://github.com/bkbilly/dbus_idle
187
+ # Version that works, using: https://github.com/bkbilly/dbus_idle
188
+ # but has many deps
187
189
  # Requires:
188
190
  # sudo apt install meson libdbus-glib-1-dev patchelf
189
191
  # pip install dbus-idle
190
192
 
191
- idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
192
- return idle_time
193
+ # idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
194
+ # return idle_time
195
+
196
+ return self.UserIdleTime.get()
197
+
193
198
 
194
199
 
195
200
  def toggle_do_not_disturb(self, widget):
@@ -210,7 +215,7 @@ class Application(Gtk.Application):
210
215
  if 'do-not-disturb' in self.session:
211
216
  return None
212
217
 
213
- afk_time = self.check_afk_time()
218
+ afk_time = self.seconds_since_user_active()
214
219
  # print("Idle time: "+str(afk_time))
215
220
 
216
221
  dbg('Last todo todo_sync_time', conf.todo_sync_time, 'Time diff',int(time_difference(conf.todo_sync_time)),'Auto refresh interval * 60', (conf.user['todolist_refresh_interval'] * 60), s="todoloading", l=3 )
@@ -224,7 +229,7 @@ class Application(Gtk.Application):
224
229
 
225
230
  if(self.is_running == False):
226
231
 
227
- if float(minutes / conf.user['randomness_interrupt_interval']).is_integer():
232
+ if num_is_multiple_of(minutes,conf.user['randomness_interrupt_interval']):
228
233
  if afk_time < 30:
229
234
 
230
235
  notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
@@ -243,7 +248,6 @@ class Application(Gtk.Application):
243
248
  # only show this once
244
249
  if afk_time < 181:
245
250
  self.open_task_window(None,{'afk_time':afk_time})
246
- # session_options_dialog(None, 'test input_data')
247
251
  else:
248
252
  if self.session['label'] in conf.user['custom_pomodoro_intervals']:
249
253
  check = conf.user['custom_pomodoro_intervals'][self.session['label']]
@@ -260,7 +264,7 @@ class Application(Gtk.Application):
260
264
  t = self.session['target']
261
265
 
262
266
  t['percent'] = round(( (t['starting_value'] + minutes) / t['value'] ) * 100,1)
263
- print("At ", t['percent'], "% of ", t['scope'], " target")
267
+ print("At", t['percent'], "% of ", t['scope'], " target")
264
268
 
265
269
  if t['type'] == 'max':
266
270
  if t['percent'] >= 100:
@@ -275,64 +279,76 @@ class Application(Gtk.Application):
275
279
  playsound('sound/xylophone-chord.mp3',False)
276
280
 
277
281
 
278
- # maybe add a target % to the session and show with tick
279
282
 
280
283
  icon_tick_number = 0
281
-
284
+
282
285
  def tick(self):
283
- menu = self.menu
284
- indicator = self.indicator
285
286
 
286
287
  # check for suspend indicated by gap in tick intervals
287
- time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
288
+ time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
288
289
  if time_since_last_tick > 10:
289
- print(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
290
-
290
+ dbg(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
291
291
  if self.is_running:
292
292
  self.open_task_window(None,{'afk_time':time_since_last_tick})
293
293
  else:
294
- print("resetting randomness timer")
295
- self.session['duration'] = 0
296
294
  self.session['start_time'] = now()
297
295
 
298
- # print("tick!")
299
- self.session['duration'] = int(time_difference(self.session['start_time']))
296
+ duration = self.session['duration'] = int(time_difference(self.session['start_time']))
300
297
 
301
- if(self.session['duration'] > 2 and (int(self.session['duration']) / 60).is_integer()):
298
+ if num_is_multiple_of(duration,60):
302
299
  self.tock()
303
300
 
304
- if(self.is_running == True):
305
- self.icon_tick_number = self.icon_tick_number + 1
301
+ if self.is_running == True:
302
+ self.icon_tick_number += 1
306
303
 
307
304
  if self.icon_tick_number > 8:
308
305
  self.icon_tick_number = 1
309
306
 
310
- label = self.session['label'] + ": " + sec_to_time(self.session['duration'])
307
+ label = self.session['label'] + ": " + sec_to_time(duration)
311
308
 
312
- indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
309
+ self.indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
313
310
 
314
311
  else:
315
312
 
316
- # label = random.choice(conf.idle_messages) # Cool but makes menu bounce around #Could be paused when the menu opens
317
313
  label = conf.user['default_text']
318
- if self.session['duration'] > 60 and self.session['duration'] % 2:
319
- indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
314
+ if duration > 60 and duration % 2:
315
+ self.indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
320
316
  else:
321
- indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
317
+ self.indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
322
318
 
323
319
  # https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
324
- indicator.set_label(label, "Wide")
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]))
320
+ self.indicator.set_label(label,label)
328
321
 
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 = {}
322
+ if num_is_multiple_of(self.icon_tick_number,3):
323
+ self.refresh_all_changed_todo_files()
324
+
333
325
  return True
334
326
 
335
327
 
328
+ def refresh_all_changed_todo_files(self):
329
+ for id, todo_config in conf.user['todolists'].items():
330
+ if todo_config['status'] and 'watch_file' in todo_config:
331
+ self.refresh_todo_if_file_changed(todo_config)
332
+
333
+
334
+ def refresh_todo_if_file_changed(self, todo_config):
335
+
336
+ try:
337
+ todo_m_time = round(os.stat(todo_config['file']).st_mtime)
338
+
339
+ if todo_config['id'] not in conf.todo_file_change_times:
340
+ conf.todo_file_change_times[todo_config['id']] = todo_m_time
341
+
342
+ elif todo_m_time != conf.todo_file_change_times[todo_config['id']]:
343
+ dbg(todo_config['id']+" file was changed!",s='todoloading')
344
+ self.async_refresh(None,todo_config)
345
+ conf.todo_file_change_times[todo_config['id']] = todo_m_time
346
+
347
+ except Exception as e:
348
+ # TODO: consider how to quietly handle errors when refresh is not prompted by the user and temporarily disabling
349
+ handle_todo_read_error(todo_config,e)
350
+
351
+
336
352
  def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
337
353
 
338
354
  if isinstance(task_data_or_id, dict):
@@ -356,7 +372,10 @@ class Application(Gtk.Application):
356
372
 
357
373
  if(self.is_running == True):
358
374
  self.stop_task()
359
-
375
+ elif(self.session['duration'] > 60):
376
+ # Log randomness session (to internal db only) if longer than one minute
377
+ utils.db_save_session(self.session)
378
+
360
379
 
361
380
  self.is_running = True
362
381
 
@@ -450,26 +469,20 @@ class Application(Gtk.Application):
450
469
  try:
451
470
 
452
471
  done_thread = threading.Thread(target=conf.todo_connectors[todolist_conf['type']].mark_task_done, args=(task,) )
472
+
453
473
  conf.todo_sync_times[todolist_conf['id']] = now() # this is to avoid causing a refresh, perhaps not the best though
454
474
 
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
- #
461
475
  done_thread.start()
462
476
 
463
477
  db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
464
478
  utils.reindex_one(task)
465
479
 
466
- # print('remove menu item')
467
480
  self.menu_tasks[task['id']].destroy()
468
-
481
+
469
482
  playsound('sound/xylophone-chord.mp3',False)
470
483
 
471
484
  except Exception as e:
472
- error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure",e )
485
+ error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure", e=e )
473
486
 
474
487
 
475
488
 
@@ -533,19 +546,12 @@ class Application(Gtk.Application):
533
546
  self.menu.show_all()
534
547
 
535
548
  self.is_running = False
536
- # print(utils.get_times(task))
537
549
  if action != 'cancel':
538
550
  notify.Notification.new("Focused on "+session['label']+" for "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
539
551
  # notify.Notification.new(action.capitalize()+" "+session['label']+" "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
540
552
 
541
553
  # Start randomness timer
542
- self.session = {
543
- "label": 'Randomness',
544
- "extended_label": 'Randomness',
545
- "start_time": now(),
546
- "duration":0,
547
- "task":{}
548
- }
554
+ self.session = default_session()
549
555
 
550
556
  self.tick()
551
557
 
@@ -554,9 +560,6 @@ class Application(Gtk.Application):
554
560
  self.menu.get_children()[0].destroy()
555
561
  self.do_not_disturb_menu_item.set_label("Do Not Disturb")
556
562
 
557
- else:
558
- print('no task running!')
559
-
560
563
 
561
564
  def task_running_menu_additions(self):
562
565
 
@@ -52,20 +52,19 @@ connectors = {
52
52
  todo_sync_time = datetime.now()
53
53
  todo_sync_times = {}
54
54
 
55
- todo_sync_required = {}
56
- file_watchers = {}
57
- file_watch_ignores = {}
55
+ todo_file_change_times = {}
56
+ timers = {}
58
57
 
59
58
  prototype_settings = {
60
59
  "pomodoro_interval": 40,
61
60
  "open_task_window_fullscreen": True,
61
+ "show_task_window_sidebars": False,
62
62
  "randomness_interrupt_interval":5,
63
63
  "default_text": "What am I doing?",
64
64
  "todolist_refresh_interval":1,
65
- "version":0.2,
65
+ "version":0.4,
66
66
  "display_todolist_as_top_level_list":'auto',
67
67
  'max_top_level_menu_items':10,
68
- # 'tick_interval':1,
69
68
  'hours_search_timeframe':'this year',
70
69
  'invoice_hourly_rate':0,
71
70
 
@@ -39,7 +39,6 @@ def add_new_task(user_conf,list,task_label):
39
39
 
40
40
  todotxt.add(task)
41
41
  todotxt.save()
42
- # task.add_project
43
42
 
44
43
  t = {
45
44
  'id':task_label,
@@ -87,6 +86,7 @@ def get_todos(user_conf):
87
86
  }
88
87
  }
89
88
 
89
+ priority_letter_to_number_map = {'A':1,'B':2,'C':3,'D':4}
90
90
 
91
91
  todotxt = pytodotxt.TodoTxt(user_conf['file'])
92
92
  for t in todotxt.parse():
@@ -99,7 +99,6 @@ def get_todos(user_conf):
99
99
  'parent_id':user_conf['id'],
100
100
  'parent_label':user_conf['label'],
101
101
  'status':1,
102
- # 'priority':1 max, 5 min, 0 none,
103
102
  'todolist':user_conf['id'],
104
103
  'data':t.attributes #TODO: add other things like date etc
105
104
  }
@@ -107,6 +106,12 @@ def get_todos(user_conf):
107
106
  if t.is_completed:
108
107
  tasks[id]['status'] = 0
109
108
 
109
+ if t.priority:
110
+ try:
111
+ tasks[id]['priority'] = priority_letter_to_number_map[t.priority]
112
+ except:
113
+ tasks[id]['priority'] = 5
114
+
110
115
 
111
116
  if t.projects:
112
117
  l = t.projects[0]
@@ -137,5 +142,5 @@ def get_todos(user_conf):
137
142
  def launch(user_conf, item = None, category = None):
138
143
  ''' Open todolist '''
139
144
 
140
- # It would b very nice to open a the right line number but xdg-open doesn't support that...
145
+ # It would b very nice to open the right line number but xdg-open doesn't support that...
141
146
  utils.open_external(user_conf['file'])
@@ -108,7 +108,7 @@ def mark_task_done(task):
108
108
  if data[line_no].strip().startswith('[ ]'):
109
109
  data[line_no] = data[line_no].replace('[ ]', '[x]')
110
110
  else:
111
- data[line_no] = data[line_no].replace(data[line_no].lstrip(), '[x] '+ data[line_no].lstrip() )
111
+ data[line_no] = data[line_no].replace(data[line_no].lstrip(), '[x]'+ data[line_no].lstrip() )
112
112
 
113
113
  # write everything back
114
114
  with open(file_uri, 'w') as file:
@@ -176,7 +176,18 @@ def get_todos(user_conf):
176
176
  status = 0
177
177
 
178
178
  label = label.removeprefix('[ ]').removeprefix('[]').strip()
179
- #TODO: use markdown title syntax as list name in addition to indentation
179
+ #TODO: use markdown title syntax as list name in addition to indentation?
180
+
181
+ # tags = ''
182
+ # # hashtag tagging
183
+ # if label.split("#").len() > 1:
184
+ # parts = label.split("#")
185
+ # print("label has hashtag",parts)
186
+
187
+ # label = parts[0].strip()
188
+ # del parts[0]
189
+ # tags = ",".join(parts)
190
+
180
191
 
181
192
  indent = len(line) - len(line.lstrip())
182
193
  indent_str = line[0:indent]
@@ -229,6 +240,7 @@ def get_todos(user_conf):
229
240
  'todolist':user_conf['id'],
230
241
  'status': status,
231
242
  'priority': priority,
243
+ # 'tags':tags,
232
244
  'data':{
233
245
  'line_no':line_no,
234
246
  'original_line':line,
@@ -87,9 +87,9 @@ class NewTaskWDialog(Gtk.Dialog):
87
87
  todolist_conf = conf.user['todolists'][parent_list['todolist']]
88
88
  try:
89
89
 
90
- conf.file_watch_ignores[todolist_conf['id']] = True
91
90
  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']]
91
+
92
+ clear_todo_file_change_time(todolist_conf)
93
93
 
94
94
  dbg('connector add task response',task)
95
95
 
@@ -121,9 +121,8 @@ class NewTaskWDialog(Gtk.Dialog):
121
121
  if conf.debug_level > 1:
122
122
  raise e
123
123
 
124
- error_notice('Error adding tasks',"Adding "+ task_label+" to "+todolist_conf['label']+" had a serious failure",e )
124
+ error_notice('Error adding tasks',"Adding "+ task_label+" to "+todolist_conf['label']+" had a serious failure",e) # NOTE: e, in the case of a key error, only prints the bad key, not the error type
125
125
 
126
-
127
126
 
128
127
  elif response == Gtk.ResponseType.CANCEL:
129
128
  print("Cancel button clicked")