nowfocus 0.4.0__py3-none-any.whl → 0.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nowfocus/__main__.py +168 -153
- nowfocus/conf.py +41 -48
- nowfocus/connectors/vikunja.py +16 -17
- nowfocus/desktop-extras/nowfocus.desktop +1 -1
- nowfocus/example-todo.txt +1 -2
- nowfocus/install.py +92 -0
- nowfocus/session_options.py +1 -1
- nowfocus/task_window.py +42 -22
- nowfocus/utils.py +68 -42
- nowfocus/version_migrator.py +20 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.4.dist-info}/METADATA +21 -28
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.4.dist-info}/RECORD +16 -14
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.4.dist-info}/WHEEL +0 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.4.dist-info}/entry_points.txt +0 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.4.dist-info}/licenses/LICENSE +0 -0
- {nowfocus-0.4.0.dist-info → nowfocus-0.4.4.dist-info}/top_level.txt +0 -0
nowfocus/__main__.py
CHANGED
|
@@ -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
|
|
|
@@ -57,85 +55,64 @@ setproctitle.setproctitle(conf.app_name)
|
|
|
57
55
|
print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file__)))
|
|
58
56
|
|
|
59
57
|
class Application(Gtk.Application):
|
|
58
|
+
icon_tick_number = 0
|
|
59
|
+
|
|
60
60
|
def __init__(self, *args, **kwargs):
|
|
61
61
|
super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
|
|
62
62
|
|
|
63
|
-
|
|
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.
|
|
63
|
+
try:
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# return None
|
|
71
|
-
|
|
72
|
-
self.window = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
self.window = None
|
|
73
68
|
|
|
74
|
-
|
|
75
|
-
|
|
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()
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
self.menu = Gtk.Menu()
|
|
76
|
+
self.menu_tasks = {}
|
|
77
|
+
self.list_menus = {}
|
|
79
78
|
|
|
80
|
-
|
|
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)
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
if conf.is_first_load:
|
|
84
|
+
import install
|
|
85
|
+
install.run_first_load_actions()
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
# menu.set_reserve_toggle_size(False) # skip menu left padding, doesn't work
|
|
87
|
+
utils.db_schema_update()
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
utils.db_schema_update()
|
|
89
|
+
self.async_refresh()
|
|
89
90
|
|
|
91
|
+
# utils.db_cleanup()
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
self.async_refresh()
|
|
93
|
+
main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
self.resume_session_from_db()
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
self.start_pipe()
|
|
98
|
+
self.open_task_window()
|
|
97
99
|
|
|
98
|
-
|
|
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()
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
signal.signal(signal.SIGINT, self.quit)
|
|
108
|
+
signal.signal(signal.SIGUSR1, self.signal_handler)
|
|
109
|
+
signal.signal(signal.SIGUSR2, self.signal_handler)
|
|
102
110
|
|
|
103
|
-
|
|
104
|
-
s = json.loads(db_session[0]['value'])
|
|
105
|
-
s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
|
|
106
|
-
self.session = s
|
|
107
|
-
self.is_running = True
|
|
108
|
-
dbg("resuming session",s['label'],l=2, s='session')
|
|
111
|
+
Gtk.main()
|
|
109
112
|
except Exception as e:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
self.pipethread = threading.Thread(target=self.check_pipe)
|
|
115
|
-
self.pipethread.daemon = True
|
|
116
|
-
self.pipethread.start()
|
|
117
|
-
|
|
118
|
-
# Testing
|
|
119
|
-
# time.sleep(3)
|
|
120
|
-
# self.open_session_options_dialog('test_param')
|
|
121
|
-
|
|
122
|
-
# self.open_settings_window() #for testing
|
|
123
|
-
# self.open_task_window() #for testing
|
|
124
|
-
|
|
125
|
-
# time.sleep(2)
|
|
126
|
-
# self.open_new_task_dialog() #for testing
|
|
127
|
-
|
|
128
|
-
# time.sleep(2)
|
|
129
|
-
# self.print_time_totals()
|
|
130
|
-
|
|
131
|
-
# signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
132
|
-
signal.signal(signal.SIGINT, self.quit)
|
|
133
|
-
signal.signal(signal.SIGUSR1, self.signal_handler)
|
|
134
|
-
signal.signal(signal.SIGUSR2, self.signal_handler)
|
|
135
|
-
|
|
136
|
-
Gtk.main()
|
|
137
|
-
|
|
138
|
-
|
|
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()
|
|
139
116
|
|
|
140
117
|
|
|
141
118
|
def print_time_totals(self = None, widget = None):
|
|
@@ -176,25 +153,13 @@ class Application(Gtk.Application):
|
|
|
176
153
|
exit()
|
|
177
154
|
|
|
178
155
|
|
|
179
|
-
def
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
# Requires xprintidle (sudo apt install xprintidle)
|
|
185
|
-
# idle_time = int(int(subprocess.getoutput('xprintidle')) / 1000)
|
|
186
|
-
|
|
187
|
-
# Version that works, using: https://github.com/bkbilly/dbus_idle
|
|
188
|
-
# but has many deps
|
|
189
|
-
# Requires:
|
|
190
|
-
# sudo apt install meson libdbus-glib-1-dev patchelf
|
|
191
|
-
# pip install dbus-idle
|
|
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()
|
|
192
161
|
|
|
193
|
-
|
|
194
|
-
# return idle_time
|
|
195
|
-
|
|
196
|
-
return self.UserIdleTime.get()
|
|
197
|
-
|
|
162
|
+
return self.seconds_since_user_active
|
|
198
163
|
|
|
199
164
|
|
|
200
165
|
def toggle_do_not_disturb(self, widget):
|
|
@@ -212,84 +177,106 @@ class Application(Gtk.Application):
|
|
|
212
177
|
|
|
213
178
|
def tock(self):
|
|
214
179
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
afk_time = self.seconds_since_user_active()
|
|
219
|
-
# 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))
|
|
220
182
|
|
|
221
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 )
|
|
222
184
|
|
|
223
|
-
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
|
|
224
187
|
self.async_refresh()
|
|
225
188
|
|
|
226
|
-
# TODO: use individual todo_refresh_times
|
|
227
|
-
|
|
228
189
|
minutes = (int(self.session['duration']) / 60)
|
|
229
190
|
|
|
230
|
-
if
|
|
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
|
+
|
|
231
210
|
|
|
232
|
-
if
|
|
233
|
-
|
|
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()
|
|
234
226
|
|
|
235
|
-
|
|
227
|
+
playsound('sound/xylophone-chord.mp3',False)
|
|
236
228
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
229
|
+
elif num_is_multiple_of(minutes,conf.user['randomness_interrupt_interval']):
|
|
230
|
+
|
|
231
|
+
notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
|
|
232
|
+
|
|
233
|
+
self.open_task_window()
|
|
234
|
+
|
|
235
|
+
playsound('sound/dinner-bell.mp3',False)
|
|
240
236
|
|
|
241
|
-
elif afk_time > 120:
|
|
242
|
-
self.session['duration'] = 0
|
|
243
|
-
self.session['start_time'] = now()
|
|
244
|
-
print("Idle time reset. afk:", afk_time, self.session)
|
|
245
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
|
|
246
243
|
else:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if afk_time < 181:
|
|
250
|
-
self.open_task_window(None,{'afk_time':afk_time})
|
|
251
|
-
else:
|
|
252
|
-
if self.session['label'] in conf.user['custom_pomodoro_intervals']:
|
|
253
|
-
check = conf.user['custom_pomodoro_intervals'][self.session['label']]
|
|
254
|
-
else:
|
|
255
|
-
check = conf.user['pomodoro_interval']
|
|
256
|
-
|
|
257
|
-
if float(minutes / check ).is_integer():
|
|
258
|
-
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
|
|
259
246
|
|
|
260
|
-
playsound('sound/bell-xylophone-g.mp3',False)
|
|
261
247
|
|
|
248
|
+
def handle_device_became_inactive(self):
|
|
249
|
+
self.user_last_inactive = now()
|
|
262
250
|
|
|
263
|
-
|
|
264
|
-
|
|
251
|
+
user_inactive_time = self.get_seconds_since_user_active()
|
|
252
|
+
dbg('device_became_inactive ',l=-1)
|
|
265
253
|
|
|
266
|
-
|
|
267
|
-
print("At", t['percent'], "% of ", t['scope'], " target")
|
|
254
|
+
if self.is_running and 'do-not-disturb' not in self.session:
|
|
268
255
|
|
|
269
|
-
|
|
270
|
-
if t['percent'] >= 100:
|
|
256
|
+
self.open_task_window(None,{'user_inactive_time':user_inactive_time})
|
|
271
257
|
|
|
272
|
-
|
|
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)
|
|
273
262
|
|
|
274
|
-
|
|
263
|
+
self.session['duration'] = 0
|
|
264
|
+
self.session['start_time'] = now()
|
|
265
|
+
dbg("Recording:", user_inactive_time, self.session)
|
|
275
266
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
playsound('sound/xylophone-chord.mp3',False)
|
|
267
|
+
if 'do-not-disturb' not in self.session and conf.user['randomness_interrupt_interval'] > 0:
|
|
268
|
+
self.open_task_window(None)
|
|
280
269
|
|
|
281
|
-
|
|
282
270
|
|
|
283
|
-
icon_tick_number = 0
|
|
284
|
-
|
|
285
271
|
def tick(self):
|
|
286
272
|
|
|
287
273
|
# check for suspend indicated by gap in tick intervals
|
|
288
274
|
time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
|
|
289
275
|
if time_since_last_tick > 10:
|
|
276
|
+
|
|
290
277
|
dbg(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
|
|
291
278
|
if self.is_running:
|
|
292
|
-
self.open_task_window(None,{'
|
|
279
|
+
self.open_task_window(None,{'user_inactive_time':time_since_last_tick})
|
|
293
280
|
else:
|
|
294
281
|
self.session['start_time'] = now()
|
|
295
282
|
|
|
@@ -306,25 +293,44 @@ class Application(Gtk.Application):
|
|
|
306
293
|
|
|
307
294
|
label = self.session['label'] + ": " + sec_to_time(duration)
|
|
308
295
|
|
|
309
|
-
|
|
310
|
-
|
|
296
|
+
icon = 'icon-'+str(self.icon_tick_number)+'.svg'
|
|
297
|
+
|
|
311
298
|
else:
|
|
312
299
|
|
|
313
|
-
label = conf.user['default_text']
|
|
314
|
-
|
|
315
|
-
|
|
300
|
+
label = conf.user['default_text'] +" "+ sec_to_time(duration)
|
|
301
|
+
|
|
302
|
+
if duration > 60 and num_is_multiple_of(duration, 2):
|
|
303
|
+
icon = 'icon-red.svg'
|
|
316
304
|
else:
|
|
317
|
-
|
|
305
|
+
icon = 'icon-1.svg'
|
|
318
306
|
|
|
319
307
|
# https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
|
|
308
|
+
|
|
309
|
+
self.indicator.set_icon_full(os.path.abspath('icon/'+icon),label)
|
|
320
310
|
self.indicator.set_label(label,label)
|
|
321
311
|
|
|
322
|
-
if num_is_multiple_of(
|
|
323
|
-
self.
|
|
324
|
-
|
|
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")
|
|
321
|
+
|
|
325
322
|
return True
|
|
326
323
|
|
|
327
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
|
+
|
|
328
334
|
def refresh_all_changed_todo_files(self):
|
|
329
335
|
for id, todo_config in conf.user['todolists'].items():
|
|
330
336
|
if todo_config['status'] and 'watch_file' in todo_config:
|
|
@@ -349,6 +355,20 @@ class Application(Gtk.Application):
|
|
|
349
355
|
handle_todo_read_error(todo_config,e)
|
|
350
356
|
|
|
351
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
|
+
|
|
352
372
|
def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
|
|
353
373
|
|
|
354
374
|
if isinstance(task_data_or_id, dict):
|
|
@@ -428,7 +448,7 @@ class Application(Gtk.Application):
|
|
|
428
448
|
|
|
429
449
|
|
|
430
450
|
def run_task_command(self,command):
|
|
431
|
-
|
|
451
|
+
dbg("running task command:", command,l=-1)
|
|
432
452
|
|
|
433
453
|
self.running_command_task_label = copy.copy(self.session['label'])
|
|
434
454
|
|
|
@@ -580,7 +600,7 @@ class Application(Gtk.Application):
|
|
|
580
600
|
|
|
581
601
|
def async_refresh(self, w=None, single_todo = None):
|
|
582
602
|
|
|
583
|
-
self.indicator.set_label("Refreshing Todolists", "
|
|
603
|
+
self.indicator.set_label("Refreshing Todolists", "Refreshing Todolists")
|
|
584
604
|
menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
|
|
585
605
|
self.menu.append(menu_item)
|
|
586
606
|
self.menu.show_all()
|
|
@@ -733,24 +753,20 @@ class Application(Gtk.Application):
|
|
|
733
753
|
dbg('Signal received',sig,s='signals')
|
|
734
754
|
|
|
735
755
|
if sig == signal.SIGUSR1:
|
|
736
|
-
# try:
|
|
737
|
-
# print('TaskWindow._instance',TaskWindow._instance)
|
|
738
|
-
# if TaskWindow._instance:
|
|
739
|
-
# self.taskwindow.destroy()
|
|
740
|
-
# else:
|
|
741
|
-
# self.open_task_window()
|
|
742
|
-
|
|
743
|
-
# except AttributeError:
|
|
744
756
|
self.open_task_window()
|
|
745
|
-
|
|
746
757
|
elif sig == signal.SIGUSR2:
|
|
747
758
|
self.open_session_options_dialog()
|
|
748
|
-
|
|
749
759
|
else:
|
|
750
760
|
dbg("no handler for received signal",s='signals',l=3)
|
|
751
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
|
+
|
|
752
769
|
def check_pipe(self):
|
|
753
|
-
# print("Listening to pipe at ",conf.pipe)
|
|
754
770
|
|
|
755
771
|
try:
|
|
756
772
|
with open(conf.pipe, "r") as pipe:
|
|
@@ -820,7 +836,6 @@ def startup():
|
|
|
820
836
|
except Exception as e:
|
|
821
837
|
print(e)
|
|
822
838
|
|
|
823
|
-
|
|
824
839
|
try:
|
|
825
840
|
os.mkfifo(conf.pipe)
|
|
826
841
|
dbg("Named pipe created successfully!", s="cli")
|
|
@@ -836,7 +851,7 @@ def startup():
|
|
|
836
851
|
|
|
837
852
|
|
|
838
853
|
except FileExistsError:
|
|
839
|
-
|
|
854
|
+
print("Named pipe exists, application must be running (or improperly shut down.) ")
|
|
840
855
|
|
|
841
856
|
# if args: pass to pipe and exit
|
|
842
857
|
if args.task:
|
nowfocus/conf.py
CHANGED
|
@@ -18,11 +18,14 @@ user_data_dir = GLib.get_user_data_dir()+"/"+app_id
|
|
|
18
18
|
Path(user_data_dir).mkdir(parents=True, exist_ok=True)
|
|
19
19
|
|
|
20
20
|
user_settings_dir = GLib.get_user_config_dir()+"/"+app_id
|
|
21
|
-
|
|
21
|
+
''' Depreciated. use user_data_dir instead '''
|
|
22
22
|
|
|
23
|
+
db_file = user_data_dir+"/data.db"
|
|
24
|
+
settings_file = user_data_dir+"/nowfocus-settings.json"
|
|
23
25
|
debug_level = 1 # dev value
|
|
24
26
|
debug_systems = []
|
|
25
|
-
pipe = "/tmp/"+app_id+"-pipe"
|
|
27
|
+
pipe = "/tmp/"+app_id+"-pipe"
|
|
28
|
+
is_first_load = False
|
|
26
29
|
|
|
27
30
|
# key and type must be the same, (Seems redundant but it's is quite helpful)
|
|
28
31
|
connectors = {
|
|
@@ -59,9 +62,10 @@ prototype_settings = {
|
|
|
59
62
|
"pomodoro_interval": 40,
|
|
60
63
|
"open_task_window_fullscreen": True,
|
|
61
64
|
"show_task_window_sidebars": False,
|
|
62
|
-
"randomness_interrupt_interval":
|
|
65
|
+
"randomness_interrupt_interval":7, #minutes
|
|
66
|
+
"device_not_in_use_threshold":3.5, #minutes
|
|
63
67
|
"default_text": "What am I doing?",
|
|
64
|
-
"todolist_refresh_interval":1,
|
|
68
|
+
"todolist_refresh_interval":1, #hours
|
|
65
69
|
"version":0.4,
|
|
66
70
|
"display_todolist_as_top_level_list":'auto',
|
|
67
71
|
'max_top_level_menu_items':10,
|
|
@@ -69,7 +73,7 @@ prototype_settings = {
|
|
|
69
73
|
'invoice_hourly_rate':0,
|
|
70
74
|
|
|
71
75
|
"custom_pomodoro_intervals": {
|
|
72
|
-
"email":7
|
|
76
|
+
"email":7 #minutes
|
|
73
77
|
},
|
|
74
78
|
|
|
75
79
|
'prompts':'What am I doing?\nWhy am I here?\nWhat could I do?\nWhat do I wish to accomplish?\nWhat is my aim?\nWhat\'s next',
|
|
@@ -84,11 +88,11 @@ prototype_settings = {
|
|
|
84
88
|
},
|
|
85
89
|
|
|
86
90
|
"todolists": {
|
|
87
|
-
"
|
|
88
|
-
"id":"
|
|
91
|
+
"Nowfocus Todo":{
|
|
92
|
+
"id":"Nowfocus Todo",
|
|
89
93
|
"type": "txt",
|
|
90
|
-
"label": "
|
|
91
|
-
"file":
|
|
94
|
+
"label": "Nowfocus Todo",
|
|
95
|
+
"file": user_data_dir+'/nowfocus-todo.txt',
|
|
92
96
|
"timetracker":"Example CSV",
|
|
93
97
|
"status":True
|
|
94
98
|
}
|
|
@@ -98,36 +102,43 @@ prototype_settings = {
|
|
|
98
102
|
"id": "Example CSV",
|
|
99
103
|
"label": "Example CSV",
|
|
100
104
|
"type":"csv",
|
|
101
|
-
"file":
|
|
105
|
+
"file":user_data_dir+'/nowfocus-timetracking-spreadsheet.csv',
|
|
102
106
|
"status":True
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
if os.path.isfile(
|
|
111
|
+
if os.path.isfile(settings_file):
|
|
112
|
+
with open(settings_file, "r") as file:
|
|
113
|
+
settings = json.load(file)
|
|
114
|
+
|
|
115
|
+
# Backward compatibility, check in old settings dir
|
|
116
|
+
elif os.path.isfile(user_settings_dir+"/user_settings.json"):
|
|
108
117
|
with open(user_settings_dir+"/user_settings.json", "r") as file:
|
|
109
|
-
|
|
118
|
+
settings = json.load(file)
|
|
110
119
|
else:
|
|
120
|
+
|
|
111
121
|
print("Setting up initial settings file")
|
|
112
|
-
|
|
122
|
+
is_first_load = True
|
|
123
|
+
settings = {}
|
|
113
124
|
|
|
114
125
|
|
|
115
126
|
user = {}
|
|
116
127
|
|
|
117
|
-
# Merge
|
|
128
|
+
# Merge settings with prototype settings
|
|
118
129
|
for key, val in prototype_settings.items():
|
|
119
130
|
|
|
120
|
-
if
|
|
131
|
+
if settings and key in connectors:
|
|
121
132
|
user[key] = {}
|
|
122
133
|
|
|
123
134
|
# merge and validate todolist and timetracker settings against connector prototypes
|
|
124
|
-
for c_key, c_val in
|
|
135
|
+
for c_key, c_val in settings[key].items():
|
|
125
136
|
|
|
126
137
|
if c_val['type'] in connectors[key]:
|
|
127
138
|
proto = copy.copy(connectors[key][c_val['type']])
|
|
128
139
|
user[key][c_key] = proto
|
|
129
140
|
else:
|
|
130
|
-
print('ERROR: no connector for type '+c_val['type']+' in
|
|
141
|
+
print('ERROR: no connector for type '+c_val['type']+' in settings_file','skipping that connector',c_val)
|
|
131
142
|
continue
|
|
132
143
|
|
|
133
144
|
# print('validate user_settings connector against prototype.','user',c_val,'proto',proto)
|
|
@@ -142,46 +153,28 @@ for key, val in prototype_settings.items():
|
|
|
142
153
|
else:
|
|
143
154
|
print("Adding missing: connection field "+p_field+")")
|
|
144
155
|
|
|
145
|
-
elif key in
|
|
146
|
-
user[key] =
|
|
156
|
+
elif key in settings and type(settings[key]) == type(val):
|
|
157
|
+
user[key] = settings[key]
|
|
147
158
|
else:
|
|
148
159
|
user[key] = val
|
|
149
160
|
|
|
150
161
|
# print(json.dumps(user, indent=4))
|
|
151
162
|
# print(json.dumps(connectors['todolists'], indent=4))
|
|
152
163
|
|
|
153
|
-
# update time_target format
|
|
154
|
-
for id, tt in user['time_targets']['lists'].items():
|
|
155
|
-
if 'within_value' not in tt:
|
|
156
|
-
print("Updating time target to new format ",tt)
|
|
157
|
-
tt['within_value'] = tt['num_days']
|
|
158
|
-
tt['within_unit'] = 'days'
|
|
159
|
-
print(tt)
|
|
160
|
-
if 'status' not in tt:
|
|
161
|
-
tt['status'] = True
|
|
162
164
|
|
|
163
|
-
|
|
164
|
-
for id, tt in user['time_targets']['tasks'].items():
|
|
165
|
-
tt
|
|
166
|
-
if 'within_value' not in tt:
|
|
167
|
-
print("Updating time target to new format ",tt)
|
|
168
|
-
tt['within_value'] = tt['num_days']
|
|
169
|
-
tt['within_unit'] = 'days'
|
|
170
|
-
print(tt)
|
|
171
|
-
if 'status' not in tt:
|
|
172
|
-
tt['status'] = True
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
with open(user_settings_dir+"/user_settings.json","w") as settings_file:
|
|
165
|
+
with open(settings_file,"w") as settings_file:
|
|
176
166
|
json.dump(user, settings_file)
|
|
177
167
|
|
|
178
|
-
|
|
179
168
|
todo_connectors = {}
|
|
180
169
|
timetracker_connectors = {}
|
|
181
170
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
try:
|
|
172
|
+
|
|
173
|
+
for id, todolist in user['todolists'].items():
|
|
174
|
+
todo_connectors[todolist['type']] = importlib.import_module('connectors.'+todolist['type'])
|
|
175
|
+
|
|
176
|
+
for id, timetracker in user['timetrackers'].items():
|
|
177
|
+
timetracker_connectors[timetracker['type']] = importlib.import_module('connectors.'+timetracker['type'])
|
|
178
|
+
except Exception as e:
|
|
179
|
+
print("error loading connector")
|
|
180
|
+
print(e)
|