nowfocus 0.4.2__tar.gz → 0.5.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 (65) hide show
  1. {nowfocus-0.4.2/src/nowfocus.egg-info → nowfocus-0.5.0}/PKG-INFO +2 -10
  2. {nowfocus-0.4.2 → nowfocus-0.5.0}/README.md +1 -9
  3. {nowfocus-0.4.2 → nowfocus-0.5.0}/pyproject.toml +1 -8
  4. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/__main__.py +158 -146
  5. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/conf.py +42 -49
  6. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/desktop-extras/nowfocus.desktop +1 -1
  7. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/example-todo.txt +1 -2
  8. nowfocus-0.5.0/src/nowfocus/install.py +92 -0
  9. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/session_options.py +1 -1
  10. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/task_window.py +6 -6
  11. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/utils.py +81 -82
  12. nowfocus-0.5.0/src/nowfocus/version_migrator.py +20 -0
  13. {nowfocus-0.4.2 → nowfocus-0.5.0/src/nowfocus.egg-info}/PKG-INFO +2 -10
  14. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus.egg-info/SOURCES.txt +2 -0
  15. {nowfocus-0.4.2 → nowfocus-0.5.0}/LICENSE +0 -0
  16. {nowfocus-0.4.2 → nowfocus-0.5.0}/setup.cfg +0 -0
  17. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/__init__.py +0 -0
  18. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/activitywatch.py +0 -0
  19. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/caldav.py +0 -0
  20. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/csv.py +0 -0
  21. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/psc_timetracker.py +0 -0
  22. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/taskwarrior.py +0 -0
  23. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/timewarrior.py +0 -0
  24. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/todo_template.py +0 -0
  25. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/todotxt.py +0 -0
  26. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/trello.py +0 -0
  27. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/txt.py +0 -0
  28. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/connectors/vikunja.py +0 -0
  29. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/desktop-extras/nowfocus.png +0 -0
  30. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/desktop-extras/nowfocus.svg +0 -0
  31. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/cancel.png +0 -0
  32. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/cancel.svg +0 -0
  33. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/edit.png +0 -0
  34. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/edit.svg +0 -0
  35. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-0.svg +0 -0
  36. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-1.svg +0 -0
  37. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-2.svg +0 -0
  38. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-3.svg +0 -0
  39. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-4.svg +0 -0
  40. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-5.svg +0 -0
  41. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-6.svg +0 -0
  42. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-7.svg +0 -0
  43. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-8.svg +0 -0
  44. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-9.svg +0 -0
  45. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon-red.svg +0 -0
  46. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/icon.svg +0 -0
  47. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/mark-done.png +0 -0
  48. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/mark-done.svg +0 -0
  49. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/pause.png +0 -0
  50. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/pause.svg +0 -0
  51. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/settings.png +0 -0
  52. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/icon/settings.svg +0 -0
  53. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/new_task_dialog.py +0 -0
  54. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/session_edit_dialog.py +0 -0
  55. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/sessions.csv +0 -0
  56. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/settings.py +0 -0
  57. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/sound/bell-xylophone-g.mp3 +0 -0
  58. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/sound/dinner-bell.mp3 +0 -0
  59. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/sound/xylophone-chord.mp3 +0 -0
  60. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/styles.css +0 -0
  61. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus/user_idle_time.py +0 -0
  62. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus.egg-info/dependency_links.txt +0 -0
  63. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus.egg-info/entry_points.txt +0 -0
  64. {nowfocus-0.4.2 → nowfocus-0.5.0}/src/nowfocus.egg-info/requires.txt +0 -0
  65. {nowfocus-0.4.2 → nowfocus-0.5.0}/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.4.2
3
+ Version: 0.5.0
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
@@ -112,13 +112,6 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
112
112
  # Install nowfocus
113
113
  pipx install nowfocus
114
114
 
115
- # Enter application directory
116
- cd ~/.local/share/pipx/venvs/nowfocus/lib/python3.12/site-packages/nowfocus/desktop-extras
117
-
118
- # Copy desktop file and icon to /usr/share
119
- sudo cp nowfocus.desktop /usr/share/applications/nowfocus.desktop
120
- sudo cp nowfocus.svg /usr/share/icons/hicolor/scalable/apps/nowfocus.svg
121
-
122
115
  # and now Focus!
123
116
  nowfocus
124
117
 
@@ -130,7 +123,6 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
130
123
  ```
131
124
 
132
125
 
133
-
134
126
  3. Add the following command to your startup applications:
135
127
  ```
136
128
  nowfocus --force
@@ -178,7 +170,7 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
178
170
 
179
171
  ### Install From Source
180
172
  ```
181
- git clone https://codeberg.org/AltruistEnterprises/nowfocus/nowfocus.git
173
+ git clone https://codeberg.org/AltruistEnterprises/nowfocus.git
182
174
  cd nowfocus
183
175
  python3 -m venv .venv/nowfocus-build
184
176
  source .venv/nowfocus-build/bin/activate
@@ -69,13 +69,6 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
69
69
  # Install nowfocus
70
70
  pipx install nowfocus
71
71
 
72
- # Enter application directory
73
- cd ~/.local/share/pipx/venvs/nowfocus/lib/python3.12/site-packages/nowfocus/desktop-extras
74
-
75
- # Copy desktop file and icon to /usr/share
76
- sudo cp nowfocus.desktop /usr/share/applications/nowfocus.desktop
77
- sudo cp nowfocus.svg /usr/share/icons/hicolor/scalable/apps/nowfocus.svg
78
-
79
72
  # and now Focus!
80
73
  nowfocus
81
74
 
@@ -87,7 +80,6 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
87
80
  ```
88
81
 
89
82
 
90
-
91
83
  3. Add the following command to your startup applications:
92
84
  ```
93
85
  nowfocus --force
@@ -135,7 +127,7 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
135
127
 
136
128
  ### Install From Source
137
129
  ```
138
- git clone https://codeberg.org/AltruistEnterprises/nowfocus/nowfocus.git
130
+ git clone https://codeberg.org/AltruistEnterprises/nowfocus.git
139
131
  cd nowfocus
140
132
  python3 -m venv .venv/nowfocus-build
141
133
  source .venv/nowfocus-build/bin/activate
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nowfocus"
3
- version = "0.4.2"
3
+ version = "0.5.0"
4
4
  authors = [
5
5
  { name="AltruistEnterprises" },
6
6
  ]
@@ -22,15 +22,10 @@ dependencies = [
22
22
  "psutil",
23
23
  "pygobject == 3.50.0",
24
24
  "pycairo == 1.27",
25
- # "meson",
26
- # "meson-python",
27
25
  "attrs",
28
26
  "caldav",
29
27
  "certifi",
30
28
  "charset-normalizer",
31
- # "dbus-idle",
32
- # "dbus-python",
33
- # "jeepney",
34
29
  "icalendar",
35
30
  "idna",
36
31
  "pywin32>=221 ; platform_system == 'Windows'",
@@ -56,12 +51,10 @@ dependencies = [
56
51
  [project.urls]
57
52
  Homepage = "https://www.nowfocus.org"
58
53
  repository = "https://codeberg.org/AltruistEnterprises/nowfocus"
59
- # Homepage = "https://gitlab.com/GitFr33/nowfocus"
60
54
 
61
55
  [project.gui-scripts]
62
56
  nowfocus = "nowfocus.__main__:startup"
63
57
 
64
-
65
58
  # include non .py files in the dist
66
59
  [tool.setuptools.packages.find]
67
60
  where = ["src"]
@@ -29,8 +29,6 @@ except Exception as e:
29
29
  gi.require_version('AppIndicator3', '0.1')
30
30
  from gi.repository import AppIndicator3 as appindicator
31
31
 
32
- # from dbus_idle import IdleMonitor
33
-
34
32
  # Set working dir to file location
35
33
  os.chdir(os.path.dirname(os.path.realpath(__file__)))
36
34
 
@@ -62,81 +60,59 @@ class Application(Gtk.Application):
62
60
  def __init__(self, *args, **kwargs):
63
61
  super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
64
62
 
65
- # try:
66
- # # To put everything here...
67
- # # this doesn't work because as soon as an exception occurs ii jumps to the handler and breaks all the following code.
63
+ try:
68
64
 
69
- # except Exception as e:
70
- # 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 ')
71
- # traceback.print_tb(e.__traceback__)
72
- # return None
73
-
74
- self.window = None
65
+
66
+
67
+ self.window = None
75
68
 
76
- self.is_running = False
77
- self.session = default_session()
69
+ self.is_running = False
70
+ self.device_in_use = True
71
+ self.UserIdleTime = UserIdleTime()
72
+ self.last_user_idle_check_time = None
73
+ self.session = default_session()
78
74
 
79
- self.menu_tasks = {}
80
- self.list_menus = {}
75
+ self.menu = Gtk.Menu()
76
+ self.menu_tasks = {}
77
+ self.list_menus = {}
81
78
 
82
- self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-1.svg'), appindicator.IndicatorCategory.APPLICATION_STATUS)
79
+ self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-1.svg'), appindicator.IndicatorCategory.APPLICATION_STATUS)
80
+ self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
81
+ self.indicator.set_menu(self.menu)
83
82
 
84
- self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
83
+ if conf.is_first_load:
84
+ import install
85
+ install.run_first_load_actions()
85
86
 
86
- self.menu = Gtk.Menu()
87
- # menu.set_reserve_toggle_size(False) # skip menu left padding, doesn't work
87
+ utils.db_schema_update()
88
88
 
89
- utils.db_init()
90
- utils.db_schema_update()
89
+ self.async_refresh()
91
90
 
91
+ # utils.db_cleanup()
92
92
 
93
- # self.update_menu()
94
- self.async_refresh()
93
+ main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
95
94
 
96
- # utils.db_cleanup()
95
+ self.resume_session_from_db()
97
96
 
98
- self.indicator.set_menu(self.menu)
97
+ self.start_pipe()
98
+ self.open_task_window()
99
99
 
100
- main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
100
+ # Testing
101
+ # time.sleep(3)
102
+ # self.open_session_options_dialog('test_param')
103
+ # self.open_settings_window() #for testing
104
+ # self.open_new_task_dialog() #for testing
105
+ # self.print_time_totals()
101
106
 
102
- try:
103
- db_session = db_query("SELECT value FROM system WHERE field = 'session'")
107
+ signal.signal(signal.SIGINT, self.quit)
108
+ signal.signal(signal.SIGUSR1, self.signal_handler)
109
+ signal.signal(signal.SIGUSR2, self.signal_handler)
104
110
 
105
- if db_session:
106
- s = json.loads(db_session[0]['value'])
107
- s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
108
- self.session = s
109
- self.is_running = True
110
- dbg("resuming session",s['label'],l=2, s='session')
111
+ Gtk.main()
111
112
  except Exception as e:
112
- dbg("Error resuming session",e,l=1)
113
-
114
- self.UserIdleTime = UserIdleTime()
115
-
116
- self.pipethread = threading.Thread(target=self.check_pipe)
117
- self.pipethread.daemon = True
118
- self.pipethread.start()
119
-
120
- # Testing
121
- # time.sleep(3)
122
- # self.open_session_options_dialog('test_param')
123
-
124
- # self.open_settings_window() #for testing
125
- # self.open_task_window() #for testing
126
-
127
- # time.sleep(2)
128
- # self.open_new_task_dialog() #for testing
129
-
130
- # time.sleep(2)
131
- # self.print_time_totals()
132
-
133
- # signal.signal(signal.SIGINT, signal.SIG_DFL)
134
- signal.signal(signal.SIGINT, self.quit)
135
- signal.signal(signal.SIGUSR1, self.signal_handler)
136
- signal.signal(signal.SIGUSR2, self.signal_handler)
137
-
138
- Gtk.main()
139
-
113
+ 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 ')
114
+ traceback.print_tb(e.__traceback__)
115
+ self.quit()
140
116
 
141
117
 
142
118
  def print_time_totals(self = None, widget = None):
@@ -177,25 +153,13 @@ class Application(Gtk.Application):
177
153
  exit()
178
154
 
179
155
 
180
- def seconds_since_user_active(self):
181
- # returns seconds of inactivity
182
- # See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
156
+ def get_seconds_since_user_active(self):
157
+
158
+ if self.last_user_idle_check_time != now().replace(microsecond=0):
159
+ self.last_user_idle_check_time = now().replace(microsecond=0)
160
+ self.seconds_since_user_active = self.UserIdleTime.get()
183
161
 
184
- # Works on x11 but not wayland
185
- # Requires xprintidle (sudo apt install xprintidle)
186
- # idle_time = int(int(subprocess.getoutput('xprintidle')) / 1000)
187
-
188
- # Version that works, using: https://github.com/bkbilly/dbus_idle
189
- # but has many deps
190
- # Requires:
191
- # sudo apt install meson libdbus-glib-1-dev patchelf
192
- # pip install dbus-idle
193
-
194
- # idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
195
- # return idle_time
196
-
197
- return self.UserIdleTime.get()
198
-
162
+ return self.seconds_since_user_active
199
163
 
200
164
 
201
165
  def toggle_do_not_disturb(self, widget):
@@ -213,81 +177,106 @@ class Application(Gtk.Application):
213
177
 
214
178
  def tock(self):
215
179
 
216
- if 'do-not-disturb' in self.session:
217
- return None
218
-
219
- afk_time = self.seconds_since_user_active()
220
- # print("Idle time: "+str(afk_time))
180
+ user_inactive_time = self.get_seconds_since_user_active()
181
+ # print("user_inactive_time: "+str(user_inactive_time))
221
182
 
222
183
  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 )
223
184
 
224
- if (int(time_difference(conf.todo_sync_time)) / 60) > conf.user['todolist_refresh_interval'] * 60 :
185
+ if (int(time_difference(conf.todo_sync_time)) / 60) > conf.user['todolist_refresh_interval'] * 60:
186
+ # TODO: use individual todo_refresh_times
225
187
  self.async_refresh()
226
188
 
227
- # TODO: use individual todo_refresh_times
228
-
229
189
  minutes = (int(self.session['duration']) / 60)
230
190
 
231
- if(self.is_running == False):
191
+ if self.check_device_became_inactive():
192
+ self.handle_device_became_inactive()
193
+
194
+ elif self.device_in_use == False or 'do-not-disturb' in self.session:
195
+ # Do nothing
196
+ dbg("Do not disturb is active")
197
+
198
+ elif self.is_running:
199
+
200
+ if self.session['label'] in conf.user['custom_pomodoro_intervals']:
201
+ check = conf.user['custom_pomodoro_intervals'][self.session['label']]
202
+ else:
203
+ check = conf.user['pomodoro_interval']
204
+
205
+ if float(minutes / check ).is_integer():
206
+ notify.Notification.new("Time for a Break?","You've been working on "+self.session['label']+" for "+str(minutes)+" minutes. ", None).show()
207
+
208
+ playsound('sound/bell-xylophone-g.mp3',False)
209
+
232
210
 
233
- if num_is_multiple_of(minutes,conf.user['randomness_interrupt_interval']):
234
- if afk_time < 30:
211
+ if 'target' in self.session:
212
+ t = self.session['target']
213
+
214
+ t['percent'] = round(( (t['starting_value'] + minutes) / t['value'] ) * 100,1)
215
+ print("At", t['percent'], "% of ", t['scope'], " target")
216
+
217
+ if t['type'] == 'max':
218
+ if t['percent'] >= 100:
219
+
220
+ notify.Notification.new("Time is up for this "+t['scope'],"You'r at "+str(round(t['percent']))+"% of your "+str(t['value'])+" minutes in the last "+str(t['within_value'])+" "+ t['within_unit'], None).show()
221
+
222
+ playsound('sound/dinner-bell.mp3',False)
223
+
224
+ elif t['type'] == 'min' and round(t['starting_value'] + minutes) == t['value']:
225
+ 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()
226
+
227
+ playsound('sound/xylophone-chord.mp3',False)
228
+
229
+ elif num_is_multiple_of(minutes,conf.user['randomness_interrupt_interval']):
235
230
 
236
- notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
231
+ notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
237
232
 
238
- self.open_task_window()
239
-
240
- playsound('sound/dinner-bell.mp3',False)
233
+ self.open_task_window()
234
+
235
+ playsound('sound/dinner-bell.mp3',False)
241
236
 
242
- elif afk_time > 120:
243
- self.session['duration'] = 0
244
- self.session['start_time'] = now()
245
- print("Idle time reset. afk:", afk_time, self.session)
246
237
 
238
+ def check_device_became_inactive(self):
239
+ if self.get_seconds_since_user_active() > (conf.user['device_not_in_use_threshold'] * 60):
240
+ if self.device_in_use == True:
241
+ self.device_in_use = False
242
+ return True
247
243
  else:
248
- if afk_time > 120:
249
- # only show this once
250
- if afk_time < 181:
251
- self.open_task_window(None,{'afk_time':afk_time})
252
- else:
253
- if self.session['label'] in conf.user['custom_pomodoro_intervals']:
254
- check = conf.user['custom_pomodoro_intervals'][self.session['label']]
255
- else:
256
- check = conf.user['pomodoro_interval']
257
-
258
- if float(minutes / check ).is_integer():
259
- notify.Notification.new("Time for a Break?","You've been working on "+self.session['label']+" for "+str(minutes)+" minutes. ", None).show()
244
+ self.device_in_use = True
245
+ return False
260
246
 
261
- playsound('sound/bell-xylophone-g.mp3',False)
262
247
 
248
+ def handle_device_became_inactive(self):
249
+ self.user_last_inactive = now()
263
250
 
264
- if 'target' in self.session:
265
- t = self.session['target']
251
+ user_inactive_time = self.get_seconds_since_user_active()
252
+ dbg('device_became_inactive ',l=-1)
266
253
 
267
- t['percent'] = round(( (t['starting_value'] + minutes) / t['value'] ) * 100,1)
268
- print("At", t['percent'], "% of ", t['scope'], " target")
254
+ if self.is_running and 'do-not-disturb' not in self.session:
269
255
 
270
- if t['type'] == 'max':
271
- if t['percent'] >= 100:
256
+ self.open_task_window(None,{'user_inactive_time':user_inactive_time})
272
257
 
273
- notify.Notification.new("Time is up for this "+t['scope'],"You'r at "+str(round(t['percent']))+"% of your "+str(t['value'])+" minutes in the last "+str(t['within_value'])+" "+ t['within_unit'], None).show()
258
+ else:
259
+ self.session['duration'] = self.session['duration'] - user_inactive_time
260
+ if self.session['duration'] > 60:
261
+ utils.db_save_session(self.session)
262
+
263
+ self.session['duration'] = 0
264
+ self.session['start_time'] = now()
265
+ dbg("Recording:", user_inactive_time, self.session)
266
+
267
+ if 'do-not-disturb' not in self.session and conf.user['randomness_interrupt_interval'] > 0:
268
+ self.open_task_window(None)
274
269
 
275
- playsound('sound/dinner-bell.mp3',False)
276
270
 
277
- elif t['type'] == 'min' and round(t['starting_value'] + minutes) == t['value']:
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()
279
-
280
- playsound('sound/xylophone-chord.mp3',False)
281
-
282
-
283
271
  def tick(self):
284
272
 
285
273
  # check for suspend indicated by gap in tick intervals
286
274
  time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
287
275
  if time_since_last_tick > 10:
276
+
288
277
  dbg(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
289
278
  if self.is_running:
290
- self.open_task_window(None,{'afk_time':time_since_last_tick})
279
+ self.open_task_window(None,{'user_inactive_time':time_since_last_tick})
291
280
  else:
292
281
  self.session['start_time'] = now()
293
282
 
@@ -312,7 +301,6 @@ class Application(Gtk.Application):
312
301
 
313
302
  if duration > 60 and num_is_multiple_of(duration, 2):
314
303
  icon = 'icon-red.svg'
315
-
316
304
  else:
317
305
  icon = 'icon-1.svg'
318
306
 
@@ -321,13 +309,28 @@ class Application(Gtk.Application):
321
309
  self.indicator.set_icon_full(os.path.abspath('icon/'+icon),label)
322
310
  self.indicator.set_label(label,label)
323
311
 
312
+ if num_is_multiple_of(duration,5): # check every 5 seconds
313
+ if self.device_in_use:
314
+ self.refresh_all_changed_todo_files()
315
+ elif self.get_seconds_since_user_active() < 60:
316
+ self.device_in_use = True
317
+ self.handle_device_became_active()
318
+ else:
319
+ # TODO: incriment a user away timer
320
+ dbg("User is still away")
324
321
 
325
- if num_is_multiple_of(self.icon_tick_number,3):
326
- self.refresh_all_changed_todo_files()
327
-
328
322
  return True
329
323
 
330
324
 
325
+ def handle_device_became_active(self):
326
+ dbg('device_became_active! ',l=-1)
327
+ self.user_last_active = now()
328
+
329
+ if self.is_running == False:
330
+ self.session['start_time'] = now()
331
+ self.session['duration'] = 0
332
+
333
+
331
334
  def refresh_all_changed_todo_files(self):
332
335
  for id, todo_config in conf.user['todolists'].items():
333
336
  if todo_config['status'] and 'watch_file' in todo_config:
@@ -352,6 +355,20 @@ class Application(Gtk.Application):
352
355
  handle_todo_read_error(todo_config,e)
353
356
 
354
357
 
358
+ def resume_session_from_db(self):
359
+ try:
360
+ db_session = db_query("SELECT value FROM system WHERE field = 'session'")
361
+
362
+ if db_session:
363
+ s = json.loads(db_session[0]['value'])
364
+ s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
365
+ self.session = s
366
+ self.is_running = True
367
+ dbg("resuming session",s['label'],l=2, s='session')
368
+ except Exception as e:
369
+ dbg("Error resuming session",e,l=1)
370
+
371
+
355
372
  def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
356
373
 
357
374
  if isinstance(task_data_or_id, dict):
@@ -420,7 +437,7 @@ class Application(Gtk.Application):
420
437
  self.task_running_menu_additions()
421
438
 
422
439
  self.menu.show_all()
423
- print("task data",task_data['data'])
440
+ dbg("Starting",task_data['extended_label'])
424
441
 
425
442
  if task_data['id'] in conf.user['task_commands']:
426
443
  command_data = conf.user['task_commands'][task_data['id']]
@@ -431,7 +448,7 @@ class Application(Gtk.Application):
431
448
 
432
449
 
433
450
  def run_task_command(self,command):
434
- print("running task command:", command)
451
+ dbg("running task command:", command,l=-1)
435
452
 
436
453
  self.running_command_task_label = copy.copy(self.session['label'])
437
454
 
@@ -736,24 +753,20 @@ class Application(Gtk.Application):
736
753
  dbg('Signal received',sig,s='signals')
737
754
 
738
755
  if sig == signal.SIGUSR1:
739
- # try:
740
- # print('TaskWindow._instance',TaskWindow._instance)
741
- # if TaskWindow._instance:
742
- # self.taskwindow.destroy()
743
- # else:
744
- # self.open_task_window()
745
-
746
- # except AttributeError:
747
756
  self.open_task_window()
748
-
749
757
  elif sig == signal.SIGUSR2:
750
758
  self.open_session_options_dialog()
751
-
752
759
  else:
753
760
  dbg("no handler for received signal",s='signals',l=3)
754
761
 
762
+
763
+ def start_pipe(self):
764
+ self.pipethread = threading.Thread(target=self.check_pipe)
765
+ self.pipethread.daemon = True
766
+ self.pipethread.start()
767
+
768
+
755
769
  def check_pipe(self):
756
- # print("Listening to pipe at ",conf.pipe)
757
770
 
758
771
  try:
759
772
  with open(conf.pipe, "r") as pipe:
@@ -823,7 +836,6 @@ def startup():
823
836
  except Exception as e:
824
837
  print(e)
825
838
 
826
-
827
839
  try:
828
840
  os.mkfifo(conf.pipe)
829
841
  dbg("Named pipe created successfully!", s="cli")
@@ -839,7 +851,7 @@ def startup():
839
851
 
840
852
 
841
853
  except FileExistsError:
842
- dbg("Named pipe exists, application must be running (or improperly shut down.) ",s="cli")
854
+ print("Named pipe exists, application must be running (or improperly shut down.) ")
843
855
 
844
856
  # if args: pass to pipe and exit
845
857
  if args.task: