NowFocus 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nowfocus/__init__.py +68 -0
- nowfocus/__main__.py +838 -0
- nowfocus/conf.py +181 -0
- nowfocus/connectors/activitywatch.py +31 -0
- nowfocus/connectors/caldav.py +174 -0
- nowfocus/connectors/csv.py +25 -0
- nowfocus/connectors/psc_timetracker.py +290 -0
- nowfocus/connectors/taskwarrior.py +119 -0
- nowfocus/connectors/timewarrior.py +31 -0
- nowfocus/connectors/todo_template.py +68 -0
- nowfocus/connectors/todotxt.py +160 -0
- nowfocus/connectors/trello.py +233 -0
- nowfocus/connectors/txt.py +244 -0
- nowfocus/connectors/vikunja.py +307 -0
- nowfocus/example-todo.txt +13 -0
- nowfocus/icon/cancel.png +0 -0
- nowfocus/icon/cancel.svg +47 -0
- nowfocus/icon/edit.png +0 -0
- nowfocus/icon/edit.svg +1 -0
- nowfocus/icon/icon-0.png +0 -0
- nowfocus/icon/icon-0.svg +86 -0
- nowfocus/icon/icon-1.svg +86 -0
- nowfocus/icon/icon-2.svg +86 -0
- nowfocus/icon/icon-3.svg +86 -0
- nowfocus/icon/icon-4.svg +86 -0
- nowfocus/icon/icon-5.svg +86 -0
- nowfocus/icon/icon-6.svg +86 -0
- nowfocus/icon/icon-7.svg +86 -0
- nowfocus/icon/icon-8.svg +86 -0
- nowfocus/icon/icon-9.svg +86 -0
- nowfocus/icon/icon.png +0 -0
- nowfocus/icon/icon.svg +188 -0
- nowfocus/icon/mark-done.png +0 -0
- nowfocus/icon/mark-done.svg +41 -0
- nowfocus/icon/pause.png +0 -0
- nowfocus/icon/pause.svg +48 -0
- nowfocus/icon/settings.png +0 -0
- nowfocus/icon/settings.svg +4 -0
- nowfocus/icon/unused/action-unavailable-symbolic.svg +3 -0
- nowfocus/icon/unused/applications-engineering-symbolic.svg +3 -0
- nowfocus/icon/unused/background-selected-symbolic.svg +5 -0
- nowfocus/icon/unused/cancel-operation-symbolic.svg +1 -0
- nowfocus/icon/unused/checkbox-checked-symbolic (2).svg +7 -0
- nowfocus/icon/unused/checkbox-checked-symbolic.svg +4 -0
- nowfocus/icon/unused/checkbox-mixed-symbolic (2).svg +6 -0
- nowfocus/icon/unused/checkbox-mixed-symbolic.svg +4 -0
- nowfocus/icon/unused/checkbox-symbolic (2).svg +6 -0
- nowfocus/icon/unused/checkbox-symbolic.svg +4 -0
- nowfocus/icon/unused/clock-app-symbolic.svg +5 -0
- nowfocus/icon/unused/document-edit-symbolic.svg +3 -0
- nowfocus/icon/unused/document-new-symbolic.svg +5 -0
- nowfocus/icon/unused/document-open-recent-symbolic.svg +3 -0
- nowfocus/icon/unused/edit-find-symbolic.svg +3 -0
- nowfocus/icon/unused/edit-symbolic.svg +5 -0
- nowfocus/icon/unused/filter-flagged-symbolic.svg +6 -0
- nowfocus/icon/unused/format-unordered-list-symbolic.svg +5 -0
- nowfocus/icon/unused/help-about-symbolic.svg +6 -0
- nowfocus/icon/unused/list-add-symbolic.svg +3 -0
- nowfocus/icon/unused/list-remove-symbolic.svg +3 -0
- nowfocus/icon/unused/media-playback-pause.svg +46 -0
- nowfocus/icon/unused/media-playback-start-symbolic.svg +3 -0
- nowfocus/icon/unused/no-event-symbolic.svg +6 -0
- nowfocus/icon/unused/object-select-symbolic.svg +5 -0
- nowfocus/icon/unused/org.gnome.Settings-about-symbolic-2.svg +4 -0
- nowfocus/icon/unused/org.gnome.Settings-about-symbolic.svg +1 -0
- nowfocus/icon/unused/org.gnome.Settings-keyboard-symbolic.svg +1 -0
- nowfocus/icon/unused/org.gnome.Settings-notifications-symbolic (2).svg +4 -0
- nowfocus/icon/unused/org.gnome.Settings-notifications-symbolic.svg +1 -0
- nowfocus/icon/unused/org.gnome.Settings-search-symbolic-2.svg +4 -0
- nowfocus/icon/unused/org.gnome.Settings-search-symbolic.svg +1 -0
- nowfocus/icon/unused/org.gnome.Settings-system-symbolic.svg +4 -0
- nowfocus/icon/unused/org.gnome.Settings-time-symbolic.svg +7 -0
- nowfocus/icon/unused/org.gnome.Settings-wacom-symbolic.svg +1 -0
- nowfocus/icon/unused/org.gnome.clocks-symbolic.svg +5 -0
- nowfocus/icon/unused/pause-large-symbolic.svg +3 -0
- nowfocus/icon/unused/preferences-system-time-symbolic.svg +6 -0
- nowfocus/icon/unused/process-stop-symbolic.svg +3 -0
- nowfocus/icon/unused/selection-checked.svg +71 -0
- nowfocus/icon/unused/software-installed-symbolic.svg +3 -0
- nowfocus/icon/unused/stopwatch-symbolic.svg +8 -0
- nowfocus/icon/unused/system-settings-symbolic.svg +7 -0
- nowfocus/icon/unused/timer-sand-symbolic.svg +8 -0
- nowfocus/icon/unused/timer-symbolic.svg +3 -0
- nowfocus/icon/unused/user-trash-symbolic (2).svg +3 -0
- nowfocus/icon/unused/user-trash-symbolic.svg +10 -0
- nowfocus/icon/unused/view-fullscreen-symbolic.svg +8 -0
- nowfocus/icon/unused/view-more-horizontal-symbolic.svg +7 -0
- nowfocus/icon/unused/view-more-symbolic.svg +7 -0
- nowfocus/icon/unused/view-restore-symbolic.svg +3 -0
- nowfocus/icon/unused/view-sort-ascending-symbolic.svg +3 -0
- nowfocus/icon/unused/view-sort-descending-symbolic.svg +3 -0
- nowfocus/icon/unused/view-tasks-unscheduled-symbolic.svg +12 -0
- nowfocus/new_task_dialog.py +130 -0
- nowfocus/runner.sh +8 -0
- nowfocus/session_edit_dialog.py +216 -0
- nowfocus/session_options.py +169 -0
- nowfocus/sessions.csv +1 -0
- nowfocus/settings.py +982 -0
- nowfocus/sound/bell-xylophone-g.mp3 +0 -0
- nowfocus/sound/dinner-bell.mp3 +0 -0
- nowfocus/sound/xylophone-chord.mp3 +0 -0
- nowfocus/styles.css +45 -0
- nowfocus/task_window.py +640 -0
- nowfocus/utils.py +1210 -0
- nowfocus-0.2.0.dist-info/METADATA +158 -0
- nowfocus-0.2.0.dist-info/RECORD +110 -0
- nowfocus-0.2.0.dist-info/WHEEL +5 -0
- nowfocus-0.2.0.dist-info/entry_points.txt +2 -0
- nowfocus-0.2.0.dist-info/licenses/LICENSE +674 -0
- nowfocus-0.2.0.dist-info/top_level.txt +1 -0
nowfocus/__main__.py
ADDED
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess, sys
|
|
3
|
+
import signal
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from datetime import datetime, timezone, timedelta
|
|
7
|
+
import importlib
|
|
8
|
+
from urllib.request import urlopen
|
|
9
|
+
import requests
|
|
10
|
+
import threading
|
|
11
|
+
import copy
|
|
12
|
+
from playsound3 import playsound
|
|
13
|
+
import setproctitle
|
|
14
|
+
import psutil
|
|
15
|
+
import argparse
|
|
16
|
+
|
|
17
|
+
import gi
|
|
18
|
+
gi.require_version('Gtk', '3.0')
|
|
19
|
+
from gi.repository import Gtk, GLib, Gdk, Gio
|
|
20
|
+
|
|
21
|
+
gi.require_version('Notify', '0.7')
|
|
22
|
+
from gi.repository import Notify as notify
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
gi.require_version('AyatanaAppIndicator3', '0.1')
|
|
26
|
+
from gi.repository import AyatanaAppIndicator3 as appindicator
|
|
27
|
+
except Exception as e:
|
|
28
|
+
gi.require_version('AppIndicator3', '0.1')
|
|
29
|
+
from gi.repository import AppIndicator3 as appindicator
|
|
30
|
+
|
|
31
|
+
from dbus_idle import IdleMonitor
|
|
32
|
+
|
|
33
|
+
# Set working dir to file location
|
|
34
|
+
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
|
35
|
+
|
|
36
|
+
# Add working dir to path
|
|
37
|
+
sys.path.append(os.path.dirname(__file__))
|
|
38
|
+
|
|
39
|
+
# from . import conf # this works in module context but not running as pile-of-files
|
|
40
|
+
import conf # this works running as pile-of-files but not in module context without sys.path.append
|
|
41
|
+
|
|
42
|
+
import utils
|
|
43
|
+
from utils import *
|
|
44
|
+
|
|
45
|
+
from settings import SettingsWindow
|
|
46
|
+
from task_window import TaskWindow
|
|
47
|
+
from new_task_dialog import NewTaskWDialog
|
|
48
|
+
from session_options import SessionOptionsDialog
|
|
49
|
+
|
|
50
|
+
dbg(conf.user,l=3,s='user_settings')
|
|
51
|
+
|
|
52
|
+
setproctitle.setproctitle(conf.app_name)
|
|
53
|
+
pipe_file = os.path.dirname(os.path.realpath(__file__))+"/"+conf.app_id+"_pipe"
|
|
54
|
+
|
|
55
|
+
class Application(Gtk.Application):
|
|
56
|
+
def __init__(self, *args, **kwargs):
|
|
57
|
+
super().__init__(*args, application_id="org.example.myapp", **kwargs)
|
|
58
|
+
|
|
59
|
+
self.window = None
|
|
60
|
+
|
|
61
|
+
self.is_running = False
|
|
62
|
+
self.session = {
|
|
63
|
+
"label":"Randomness",
|
|
64
|
+
"extended_label": 'Randomness',
|
|
65
|
+
"start_time":datetime.now(),
|
|
66
|
+
"duration":0,
|
|
67
|
+
'task':{},
|
|
68
|
+
'notes':'',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
self.menu_tasks = {}
|
|
72
|
+
self.list_menus = {}
|
|
73
|
+
|
|
74
|
+
self.indicator = appindicator.Indicator.new(conf.app_name, os.path.abspath('icon/icon-0.svg'), appindicator.IndicatorCategory.APPLICATION_STATUS)
|
|
75
|
+
|
|
76
|
+
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
|
|
77
|
+
|
|
78
|
+
self.menu = Gtk.Menu()
|
|
79
|
+
# menu.set_reserve_toggle_size(False) # skip menu left padding, doesn't work
|
|
80
|
+
|
|
81
|
+
utils.db_init()
|
|
82
|
+
utils.db_update()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# self.update_menu()
|
|
86
|
+
self.async_refresh()
|
|
87
|
+
|
|
88
|
+
# utils.db_cleanup()
|
|
89
|
+
|
|
90
|
+
self.indicator.set_menu(self.menu)
|
|
91
|
+
|
|
92
|
+
# main_tick_timer = GLib.timeout_add_seconds(conf.user['tick_interval'], self.tick)
|
|
93
|
+
main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
db_session = db_query("SELECT value FROM system WHERE field = 'session'")
|
|
97
|
+
|
|
98
|
+
if db_session:
|
|
99
|
+
s = json.loads(db_session[0]['value'])
|
|
100
|
+
s['start_time'] = datetime.strptime(s['start_time'],'%Y-%m-%d %H:%M:%S.%f')
|
|
101
|
+
self.session = s
|
|
102
|
+
self.is_running = True
|
|
103
|
+
dbg("resuming session",s['label'],l=2, s='session')
|
|
104
|
+
except Exception as e:
|
|
105
|
+
dbg("Error resuming session",e,l=1)
|
|
106
|
+
|
|
107
|
+
self.pipethread = threading.Thread(target=self.check_pipe)
|
|
108
|
+
self.pipethread.daemon = True
|
|
109
|
+
self.pipethread.start()
|
|
110
|
+
|
|
111
|
+
# Testing
|
|
112
|
+
# time.sleep(3)
|
|
113
|
+
# self.open_session_options_dialog('test_param')
|
|
114
|
+
|
|
115
|
+
# self.open_settings_window() #for testing
|
|
116
|
+
# self.open_task_window() #for testing
|
|
117
|
+
|
|
118
|
+
# time.sleep(2)
|
|
119
|
+
# self.open_new_task_dialog() #for testing
|
|
120
|
+
|
|
121
|
+
# time.sleep(2)
|
|
122
|
+
# self.print_time_totals()
|
|
123
|
+
|
|
124
|
+
# signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
125
|
+
signal.signal(signal.SIGINT, self.quit)
|
|
126
|
+
signal.signal(signal.SIGUSR1, self.signal_handler)
|
|
127
|
+
signal.signal(signal.SIGUSR2, self.signal_handler)
|
|
128
|
+
|
|
129
|
+
Gtk.main()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def print_time_totals(self = None, widget = None):
|
|
135
|
+
|
|
136
|
+
# SELECT extended_label, (SUM(duration) / 60 ) FROM sessions GROUP BY extended_label
|
|
137
|
+
# SELECT extended_label, (SUM(duration) / 60 ) FROM sessions GROUP BY parent_id
|
|
138
|
+
|
|
139
|
+
timeframe = hours_search_timeframes(conf.user['hours_search_timeframe'])
|
|
140
|
+
|
|
141
|
+
print("\n\n\n #### SESSION TOTALS "+utils.sessions_timeframe_sql()+" ### \n\n\n")
|
|
142
|
+
|
|
143
|
+
l = ''
|
|
144
|
+
|
|
145
|
+
for t in db_query("SELECT DISTINCT id, parent_id, label, parent_label FROM tasks WHERE id IN(SELECT DISTINCT task_id FROM sessions WHERE "+utils.sessions_timeframe_sql()+") ORDER BY extended_label ASC "):
|
|
146
|
+
if t['parent_id'] and l != t['parent_id']:
|
|
147
|
+
l = t['parent_id']
|
|
148
|
+
|
|
149
|
+
print(extended_label(db_get_item_by_id(t['parent_id'],'lists')) +": "+ sec_to_time(get_total_time(l,'lists',timeframe[0],timeframe[1])))
|
|
150
|
+
|
|
151
|
+
print(" "+ t['label'] +": "+ sec_to_time(get_total_time(t['id'],'tasks',timeframe[0],timeframe[1])))
|
|
152
|
+
# def get_total_time(id, category = 'tasks', start_time = None, end_time = None, get_minutes = None):
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def quit(self, widget_or_signal_source=None, condition=None):
|
|
156
|
+
print("Adios ", conf.app_name)
|
|
157
|
+
print("widget_or_signal_source ", widget_or_signal_source)
|
|
158
|
+
print("condition ", condition)
|
|
159
|
+
|
|
160
|
+
if self.is_running:
|
|
161
|
+
print('Caching active session', self.session['label'])
|
|
162
|
+
db_set_session_cache(self.session)
|
|
163
|
+
try:
|
|
164
|
+
# print("before os.remove(pipe_file)")
|
|
165
|
+
os.remove(pipe_file)
|
|
166
|
+
print("Pipe removed")
|
|
167
|
+
except Exception as e:
|
|
168
|
+
print("Error removing pipe_file in quit",e)
|
|
169
|
+
|
|
170
|
+
notify.uninit()
|
|
171
|
+
Gtk.main_quit()
|
|
172
|
+
exit()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def check_afk_time(self):
|
|
176
|
+
# returns seconds of inactivity
|
|
177
|
+
# See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
|
|
178
|
+
|
|
179
|
+
# Works on x11 but not wayland
|
|
180
|
+
# Requires xprintidle (sudo apt install xprintidle)
|
|
181
|
+
# idle_time = int(int(subprocess.getoutput('xprintidle')) / 1000)
|
|
182
|
+
|
|
183
|
+
# Currently using: https://github.com/bkbilly/dbus_idle
|
|
184
|
+
# Requires:
|
|
185
|
+
# sudo apt install meson libdbus-glib-1-dev patchelf
|
|
186
|
+
# pip install dbus-idle
|
|
187
|
+
|
|
188
|
+
idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
|
|
189
|
+
return idle_time
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def toggle_do_not_disturb(self, widget):
|
|
193
|
+
if 'do-not-disturb' in self.session:
|
|
194
|
+
del self.session['do-not-disturb']
|
|
195
|
+
self.do_not_disturb_menu_item.set_label("Enable Do Not Disturb")
|
|
196
|
+
self.indicator.set_label("Do Not Disturb Disabled", "")
|
|
197
|
+
|
|
198
|
+
else:
|
|
199
|
+
self.session['do-not-disturb'] = True
|
|
200
|
+
self.indicator.set_label("Do Not Disturb Enabled", "")
|
|
201
|
+
self.do_not_disturb_menu_item.set_label("Disable Do Not Disturb")
|
|
202
|
+
|
|
203
|
+
return True
|
|
204
|
+
|
|
205
|
+
def tock(self):
|
|
206
|
+
|
|
207
|
+
if 'do-not-disturb' in self.session:
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
afk_time = self.check_afk_time()
|
|
211
|
+
# print("Idle time: "+str(afk_time))
|
|
212
|
+
|
|
213
|
+
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 )
|
|
214
|
+
|
|
215
|
+
if (int(time_difference(conf.todo_sync_time)) / 60) > conf.user['todolist_refresh_interval'] * 60 :
|
|
216
|
+
self.async_refresh()
|
|
217
|
+
|
|
218
|
+
minutes = (int(self.session['duration']) / 60)
|
|
219
|
+
|
|
220
|
+
if(self.is_running == False):
|
|
221
|
+
|
|
222
|
+
if float(minutes / conf.user['randomness_interrupt_interval']).is_integer():
|
|
223
|
+
if afk_time < 30:
|
|
224
|
+
|
|
225
|
+
notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
|
|
226
|
+
|
|
227
|
+
self.open_task_window()
|
|
228
|
+
|
|
229
|
+
playsound('sound/dinner-bell.mp3',False)
|
|
230
|
+
|
|
231
|
+
elif afk_time > 120:
|
|
232
|
+
self.session['duration'] = 0
|
|
233
|
+
self.session['start_time'] = now()
|
|
234
|
+
print("Idle time reset. afk:", afk_time, self.session)
|
|
235
|
+
|
|
236
|
+
else:
|
|
237
|
+
if afk_time > 120:
|
|
238
|
+
# only show this once
|
|
239
|
+
if afk_time < 181:
|
|
240
|
+
self.open_task_window(None,{'afk_time':afk_time})
|
|
241
|
+
# session_options_dialog(None, 'test input_data')
|
|
242
|
+
else:
|
|
243
|
+
if self.session['label'] in conf.user['custom_pomodoro_intervals']:
|
|
244
|
+
check = conf.user['custom_pomodoro_intervals'][self.session['label']]
|
|
245
|
+
else:
|
|
246
|
+
check = conf.user['pomodoro_interval']
|
|
247
|
+
|
|
248
|
+
if float(minutes / check ).is_integer():
|
|
249
|
+
notify.Notification.new("Time for a Break?","You've been working on "+self.session['label']+" for "+str(minutes)+" minutes. ", None).show()
|
|
250
|
+
|
|
251
|
+
playsound('sound/bell-xylophone-g.mp3',False)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
if 'target' in self.session:
|
|
255
|
+
t = self.session['target']
|
|
256
|
+
|
|
257
|
+
t['percent'] = round(( (t['starting_value'] + minutes) / t['value'] ) * 100,1)
|
|
258
|
+
print("At ", t['percent'], "% of ", t['scope'], " target")
|
|
259
|
+
|
|
260
|
+
if t['type'] == 'max':
|
|
261
|
+
if t['percent'] >= 100:
|
|
262
|
+
|
|
263
|
+
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()
|
|
264
|
+
|
|
265
|
+
playsound('sound/dinner-bell.mp3',False)
|
|
266
|
+
|
|
267
|
+
elif t['type'] == 'min' and round(t['starting_value'] + minutes) == t['value']:
|
|
268
|
+
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()
|
|
269
|
+
|
|
270
|
+
playsound('sound/xylophone-chord.mp3',False)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# maybe add a target % to the session and show with tick
|
|
274
|
+
|
|
275
|
+
icon_tick_number = 0
|
|
276
|
+
|
|
277
|
+
def tick(self):
|
|
278
|
+
menu = self.menu
|
|
279
|
+
indicator = self.indicator
|
|
280
|
+
|
|
281
|
+
# check for suspend indicated by gap in tick intervals
|
|
282
|
+
time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
|
|
283
|
+
if time_since_last_tick > 10:
|
|
284
|
+
print(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
|
|
285
|
+
|
|
286
|
+
if self.is_running:
|
|
287
|
+
self.open_task_window(None,{'afk_time':time_since_last_tick})
|
|
288
|
+
else:
|
|
289
|
+
print("resetting randomness timer")
|
|
290
|
+
self.session['duration'] = 0
|
|
291
|
+
self.session['start_time'] = now()
|
|
292
|
+
|
|
293
|
+
# print("tick!")
|
|
294
|
+
self.session['duration'] = int(time_difference(self.session['start_time']))
|
|
295
|
+
|
|
296
|
+
if(self.session['duration'] > 2 and (int(self.session['duration']) / 60).is_integer()):
|
|
297
|
+
self.tock()
|
|
298
|
+
|
|
299
|
+
if(self.is_running == True):
|
|
300
|
+
self.icon_tick_number = self.icon_tick_number + 1
|
|
301
|
+
|
|
302
|
+
if self.icon_tick_number > 9:
|
|
303
|
+
self.icon_tick_number = 1
|
|
304
|
+
|
|
305
|
+
label = self.session['label'] + ": " + sec_to_time(self.session['duration'])
|
|
306
|
+
|
|
307
|
+
indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
|
|
308
|
+
|
|
309
|
+
else:
|
|
310
|
+
|
|
311
|
+
# label = random.choice(conf.idle_messages) # Cool but makes menu bounce around #Could be paused when the menu opens
|
|
312
|
+
label = conf.user['default_text']
|
|
313
|
+
|
|
314
|
+
indicator.set_icon_full(os.path.abspath('icon/icon-0.svg'),label)
|
|
315
|
+
|
|
316
|
+
# https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
|
|
317
|
+
indicator.set_label(label, "Wide")
|
|
318
|
+
|
|
319
|
+
return True
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
|
|
323
|
+
|
|
324
|
+
if isinstance(task_data_or_id, dict):
|
|
325
|
+
task_data = task_data_or_id
|
|
326
|
+
else:
|
|
327
|
+
task_data = utils.db_get_item_by_id(task_data_or_id)
|
|
328
|
+
|
|
329
|
+
task_label = task_data['label']
|
|
330
|
+
dbg("starting "+ task_label,l=1)
|
|
331
|
+
|
|
332
|
+
if transfer_current_session_time:
|
|
333
|
+
start_time = self.session['start_time']
|
|
334
|
+
if(self.is_running == True):
|
|
335
|
+
self.stop_task(None,'cancel')
|
|
336
|
+
|
|
337
|
+
else:
|
|
338
|
+
start_time = now()
|
|
339
|
+
|
|
340
|
+
if(self.is_running == True):
|
|
341
|
+
self.stop_task()
|
|
342
|
+
|
|
343
|
+
self.is_running = True
|
|
344
|
+
|
|
345
|
+
s = {
|
|
346
|
+
"label": task_label,
|
|
347
|
+
"extended_label": utils.extended_label(task_data),
|
|
348
|
+
"start_time": start_time,
|
|
349
|
+
"duration":0,
|
|
350
|
+
"task":task_data,
|
|
351
|
+
"notes":'',
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if s['task']['id'] in conf.user['time_targets']['tasks']:
|
|
355
|
+
s['target'] = copy.copy(conf.user['time_targets']['tasks'][s['task']['id']])
|
|
356
|
+
s['target']['scope'] = 'task'
|
|
357
|
+
target_start = datetime_minus_calendar_unit(s['target']['within_unit'],s['target']['within_value'])
|
|
358
|
+
|
|
359
|
+
s['target']['starting_value'] = utils.get_total_time(task_data['id'],'task', target_start,None,"get_minutes")
|
|
360
|
+
s['target']['percent'] = s['target']['starting_value'] / s['target']['value'] * 100
|
|
361
|
+
|
|
362
|
+
if s['task']['parent_id'] in conf.user['time_targets']['lists']:
|
|
363
|
+
s['target'] = copy.copy(conf.user['time_targets']['lists'][s['task']['parent_id']])
|
|
364
|
+
s['target']['scope'] = 'list'
|
|
365
|
+
target_start = datetime_minus_calendar_unit(s['target']['within_unit'],s['target']['within_value'])
|
|
366
|
+
|
|
367
|
+
s['target']['starting_value'] = utils.get_total_time(task_data['parent_id'],'list', target_start,None,"get_minutes")
|
|
368
|
+
s['target']['percent'] = s['target']['starting_value'] / s['target']['value'] * 100
|
|
369
|
+
|
|
370
|
+
dbg("session with list target started",s,s="targets",l=3)
|
|
371
|
+
|
|
372
|
+
self.session = s
|
|
373
|
+
|
|
374
|
+
utils.db_set_session_cache(self.session)
|
|
375
|
+
|
|
376
|
+
self.tick()
|
|
377
|
+
|
|
378
|
+
# print('total times for task ',task_label, utils.get_times(task_data))
|
|
379
|
+
|
|
380
|
+
self.task_running_menu_additions()
|
|
381
|
+
|
|
382
|
+
self.menu.show_all()
|
|
383
|
+
print("task data",task_data['data'])
|
|
384
|
+
|
|
385
|
+
if task_data['id'] in conf.user['task_commands']:
|
|
386
|
+
command_data = conf.user['task_commands'][task_data['id']]
|
|
387
|
+
if command_data['status']:
|
|
388
|
+
thread = threading.Thread(target=self.run_task_command,args=(command_data['command'],))
|
|
389
|
+
print("found task command:", command_data['command'])
|
|
390
|
+
thread.start()
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def run_task_command(self,command):
|
|
394
|
+
print("running task command:", command)
|
|
395
|
+
|
|
396
|
+
self.running_command_task_label = copy.copy(self.session['label'])
|
|
397
|
+
|
|
398
|
+
# Check if already running
|
|
399
|
+
process = None
|
|
400
|
+
for proc in psutil.process_iter():
|
|
401
|
+
pinfo = proc.as_dict(attrs=['pid', 'name'])
|
|
402
|
+
print("Check process", pinfo['name'])
|
|
403
|
+
if pinfo['name'] == command:
|
|
404
|
+
process = proc
|
|
405
|
+
break
|
|
406
|
+
else:
|
|
407
|
+
continue
|
|
408
|
+
|
|
409
|
+
if process:
|
|
410
|
+
print("waiting for already running command with psutils", command)
|
|
411
|
+
process.wait()
|
|
412
|
+
else:
|
|
413
|
+
print("Launching command with subprocess.run", command)
|
|
414
|
+
subprocess.run(command)
|
|
415
|
+
|
|
416
|
+
print("running task command complete:", command)
|
|
417
|
+
|
|
418
|
+
if self.session['label'] == self.running_command_task_label:
|
|
419
|
+
GLib.idle_add(self.stop_task)
|
|
420
|
+
GLib.idle_add(self.open_task_window)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def mark_done(self, w=None, task = None):
|
|
424
|
+
''' second (task) argument is required and must be a task object '''
|
|
425
|
+
print("Mark Task done")
|
|
426
|
+
print(task)
|
|
427
|
+
|
|
428
|
+
todolist_conf = conf.user['todolists'][task['todolist']]
|
|
429
|
+
|
|
430
|
+
try:
|
|
431
|
+
done_thread = threading.Thread(target=conf.todo_connectors[todolist_conf['type']].mark_task_done, args=(task,) )
|
|
432
|
+
done_thread.start()
|
|
433
|
+
|
|
434
|
+
db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
|
|
435
|
+
utils.reindex_one(task)
|
|
436
|
+
|
|
437
|
+
# print('remove menu item')
|
|
438
|
+
self.menu_tasks[task['id']].destroy()
|
|
439
|
+
|
|
440
|
+
playsound('sound/xylophone-chord.mp3',False)
|
|
441
|
+
|
|
442
|
+
except Exception as e:
|
|
443
|
+
error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure",e )
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def stop_task(self, w = '', action = 'save', custom_end_time=None):
|
|
448
|
+
''' supported actions are save,cancel,mark_done '''
|
|
449
|
+
|
|
450
|
+
list_menus = self.list_menus
|
|
451
|
+
menu_tasks = self.menu_tasks
|
|
452
|
+
session = self.session
|
|
453
|
+
|
|
454
|
+
print("Stopping ", session['label'])
|
|
455
|
+
task = session['task']
|
|
456
|
+
if self.is_running == True:
|
|
457
|
+
session['duration'] = time_difference(session['start_time'],custom_end_time)
|
|
458
|
+
db_query("DELETE FROM system WHERE field = 'session' ")
|
|
459
|
+
|
|
460
|
+
if action != 'cancel':
|
|
461
|
+
|
|
462
|
+
# Get time tracker for this tasks todolist
|
|
463
|
+
todolist_conf = conf.user['todolists'][task['todolist']]
|
|
464
|
+
timetracker_conf = conf.user['timetrackers'][todolist_conf['timetracker']]
|
|
465
|
+
|
|
466
|
+
dbg("Save Session to "+ todolist_conf['timetracker'])
|
|
467
|
+
try:
|
|
468
|
+
save_thread = threading.Thread(target=conf.timetracker_connectors[timetracker_conf['type']].save_session, args=(session,timetracker_conf) )
|
|
469
|
+
save_thread.start()
|
|
470
|
+
except Exception as e:
|
|
471
|
+
error_notice('Error Saving Time Data'," Recording timetracking for "+ task['label']+" in "+timetracker_conf['label']+" had a serious failure",e )
|
|
472
|
+
|
|
473
|
+
session['timetracker'] = todolist_conf['timetracker']
|
|
474
|
+
|
|
475
|
+
utils.db_save_session(session)
|
|
476
|
+
|
|
477
|
+
if action == 'mark_done':
|
|
478
|
+
self.mark_done(None,task)
|
|
479
|
+
|
|
480
|
+
else:
|
|
481
|
+
print("add ",utils.extended_label(task)," to recent tasks")
|
|
482
|
+
i = Gtk.MenuItem.new_with_label(utils.extended_label(task))
|
|
483
|
+
i.connect('activate',self.start_task,task)
|
|
484
|
+
list_menus['recent'].prepend(i)
|
|
485
|
+
try:
|
|
486
|
+
list_menus['recent'].get_children()[11].destroy()
|
|
487
|
+
except Exception as e:
|
|
488
|
+
dbg("Exception trying to rotate recent tasks. probably are less than 11",l=2,s="recent")
|
|
489
|
+
|
|
490
|
+
# Check de-hoist completed time targets
|
|
491
|
+
ballance = utils.check_time_target(task)
|
|
492
|
+
dbg('time target ballance:', ballance,l=2,s='time_targets')
|
|
493
|
+
|
|
494
|
+
if ballance != False and ballance < 0:
|
|
495
|
+
dbg('un-hoist completed time target',l=2,s='time_targets')
|
|
496
|
+
try:
|
|
497
|
+
self.menu.remove(menu_tasks[task['id']]) # how to check that this actually worked/ it was hoisted?
|
|
498
|
+
list_menus[task['parent_id']].append(menu_tasks[task['id']])
|
|
499
|
+
except Exception as e:
|
|
500
|
+
# Boo Hoo?
|
|
501
|
+
dbg('de-hoisting failed',e)
|
|
502
|
+
playsound('sound/xylophone-chord.mp3',False)
|
|
503
|
+
|
|
504
|
+
self.menu.show_all()
|
|
505
|
+
|
|
506
|
+
self.is_running = False
|
|
507
|
+
# print(utils.get_times(task))
|
|
508
|
+
|
|
509
|
+
notify.Notification.new(action.capitalize()+" "+session['label']+" "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
|
|
510
|
+
|
|
511
|
+
# Start randomness timer
|
|
512
|
+
self.session = {
|
|
513
|
+
"label": 'Randomness',
|
|
514
|
+
"extended_label": 'Randomness',
|
|
515
|
+
"start_time": now(),
|
|
516
|
+
"duration":0,
|
|
517
|
+
"task":{}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
self.tick()
|
|
521
|
+
|
|
522
|
+
# Rm stop task menu item
|
|
523
|
+
self.menu.get_children()[0].destroy()
|
|
524
|
+
self.menu.get_children()[0].destroy()
|
|
525
|
+
self.do_not_disturb_menu_item.set_label("Do Not Disturb")
|
|
526
|
+
|
|
527
|
+
else:
|
|
528
|
+
print('no task running!')
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def task_running_menu_additions(self):
|
|
532
|
+
|
|
533
|
+
i = Gtk.ImageMenuItem.new_with_label("Edit Session")
|
|
534
|
+
i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/edit.svg')))
|
|
535
|
+
|
|
536
|
+
# i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/status/software-installed-symbolic.svg'))
|
|
537
|
+
i.set_always_show_image(True)
|
|
538
|
+
i.connect("activate", self.open_session_options_dialog,'from_menu')
|
|
539
|
+
self.menu.insert(i,0)
|
|
540
|
+
|
|
541
|
+
i = Gtk.ImageMenuItem.new_with_label("Pause" )
|
|
542
|
+
i.set_image(Gtk.Image.new_from_file(os.path.abspath('icon/pause.svg')))
|
|
543
|
+
|
|
544
|
+
# i.set_image(Gtk.Image.new_from_file('/usr/share/icons/Yaru/scalable/multimedia/pause-symbolic.svg'))
|
|
545
|
+
i.set_always_show_image(True)
|
|
546
|
+
i.connect("activate", self.stop_task)
|
|
547
|
+
self.menu.insert(i,0)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def async_refresh(self, w=None):
|
|
551
|
+
|
|
552
|
+
self.indicator.set_label("Refreshing Todolists", "Wide")
|
|
553
|
+
menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
|
|
554
|
+
self.menu.append(menu_item)
|
|
555
|
+
self.menu.show_all()
|
|
556
|
+
|
|
557
|
+
connectors_thread = threading.Thread(target=self.async_refresh_inner)
|
|
558
|
+
connectors_thread.start()
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def async_refresh_inner(self):
|
|
562
|
+
# dbg("async refresh started",s="todoloading",l=3)
|
|
563
|
+
utils.get_todolists()
|
|
564
|
+
dbg("async refresh complete",s="todoloading",l=3)
|
|
565
|
+
GLib.idle_add(self.update_menu)
|
|
566
|
+
|
|
567
|
+
try:
|
|
568
|
+
GLib.idle_add(self.taskwindow.refresh_search_cache)
|
|
569
|
+
except Exception:
|
|
570
|
+
pass
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def update_menu(self, w = ''):
|
|
574
|
+
# print("update_menu")
|
|
575
|
+
menu = self.menu
|
|
576
|
+
|
|
577
|
+
self.menu_tasks = {}
|
|
578
|
+
self.list_menus = {}
|
|
579
|
+
|
|
580
|
+
menu_tasks = self.menu_tasks
|
|
581
|
+
list_menus = self.list_menus
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
menu.foreach(lambda child: child.destroy())
|
|
585
|
+
|
|
586
|
+
utils.add_todos_to_menu(menu, menu_tasks, list_menus, self.start_task)
|
|
587
|
+
|
|
588
|
+
menu.append(Gtk.SeparatorMenuItem.new())
|
|
589
|
+
|
|
590
|
+
# Recent Tasks Menu
|
|
591
|
+
list_menus['recent'] = Gtk.Menu() # the sub_list that items get added to
|
|
592
|
+
recents = Gtk.MenuItem.new_with_mnemonic("Recent Tasks") # the "item" that gets added
|
|
593
|
+
recents.set_submenu(list_menus['recent'])
|
|
594
|
+
menu.append(recents)
|
|
595
|
+
|
|
596
|
+
for id, t in utils.get_recent_tasks().items():
|
|
597
|
+
# print('recent task',t)
|
|
598
|
+
try:
|
|
599
|
+
i = Gtk.MenuItem.new_with_label(utils.extended_label(t))
|
|
600
|
+
i.connect('activate',self.start_task,t)
|
|
601
|
+
list_menus['recent'].append(i)
|
|
602
|
+
|
|
603
|
+
except Exception as e:
|
|
604
|
+
# Because a key error if the task has was completed or todolist removed
|
|
605
|
+
print('recent_tasks error', e)
|
|
606
|
+
menu.append(Gtk.SeparatorMenuItem.new())
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
# Todolist openables
|
|
610
|
+
openables = []
|
|
611
|
+
for id, todolist in conf.user['todolists'].items():
|
|
612
|
+
if todolist['status']:
|
|
613
|
+
openable = get_connector_openable(None, todolist,False)
|
|
614
|
+
if openable not in openables:
|
|
615
|
+
openables.append(openable)
|
|
616
|
+
|
|
617
|
+
menu_aw = Gtk.MenuItem.new_with_label("Open "+todolist['label']+" ")
|
|
618
|
+
menu_aw.connect("activate", get_connector_openable, todolist)
|
|
619
|
+
menu.append(menu_aw)
|
|
620
|
+
|
|
621
|
+
# Timetrackers
|
|
622
|
+
for id, timetracker in conf.user['timetrackers'].items():
|
|
623
|
+
if timetracker['status']:
|
|
624
|
+
openable = get_connector_openable(None, timetracker,False)
|
|
625
|
+
if openable not in openables:
|
|
626
|
+
openables.append(openable)
|
|
627
|
+
menu_aw = Gtk.MenuItem.new_with_label("Open "+timetracker['label']+" ")
|
|
628
|
+
menu_aw.connect("activate", get_connector_openable, timetracker)
|
|
629
|
+
menu.append(menu_aw)
|
|
630
|
+
|
|
631
|
+
# Update List
|
|
632
|
+
# Lots of spaces to make menu wide-ish in preparation for opening wide sub menus
|
|
633
|
+
menu_update = Gtk.MenuItem.new_with_label("Refresh Lists ")
|
|
634
|
+
menu_update.connect("activate", self.async_refresh)
|
|
635
|
+
menu.append(menu_update)
|
|
636
|
+
|
|
637
|
+
setting_menu_item = Gtk.MenuItem.new_with_label("Settings")
|
|
638
|
+
setting_menu_item.connect("activate",self.open_settings_window)
|
|
639
|
+
menu.append(setting_menu_item)
|
|
640
|
+
|
|
641
|
+
task_window_menu_item = Gtk.MenuItem.new_with_label("Tasks")
|
|
642
|
+
task_window_menu_item.connect("activate",self.open_task_window)
|
|
643
|
+
menu.prepend(task_window_menu_item)
|
|
644
|
+
self.indicator.set_secondary_activate_target(task_window_menu_item)
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
new_task_menu_item = Gtk.MenuItem.new_with_label("New Task")
|
|
648
|
+
new_task_menu_item.connect("activate",self.open_new_task_dialog)
|
|
649
|
+
menu.append(new_task_menu_item)
|
|
650
|
+
|
|
651
|
+
self.do_not_disturb_menu_item = Gtk.MenuItem.new_with_label("Do Not Disturb")
|
|
652
|
+
if 'do-not-disturb' in self.session:
|
|
653
|
+
self.do_not_disturb_menu_item.set_label("Disable Do Not Disturb")
|
|
654
|
+
self.do_not_disturb_menu_item.connect("activate",self.toggle_do_not_disturb)
|
|
655
|
+
menu.append(self.do_not_disturb_menu_item)
|
|
656
|
+
|
|
657
|
+
quit_menu_item = Gtk.MenuItem.new_with_label("Quit")
|
|
658
|
+
quit_menu_item.connect("activate",self.quit)
|
|
659
|
+
menu.append(quit_menu_item)
|
|
660
|
+
|
|
661
|
+
if self.is_running:
|
|
662
|
+
self.task_running_menu_additions()
|
|
663
|
+
|
|
664
|
+
menu.show_all()
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def open_session_options_dialog(self, w = None, params = None):
|
|
668
|
+
|
|
669
|
+
if hasattr(self, 'session_options_dialog'):
|
|
670
|
+
# print("present existing session_options_dialog",self.session_options_dialog)
|
|
671
|
+
self.session_options_dialog.present()
|
|
672
|
+
else:
|
|
673
|
+
self.session_options_dialog = SessionOptionsDialog(self,params)
|
|
674
|
+
self.session_options_dialog.show_all()
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def open_settings_window(self, w = None, **kwargs):
|
|
678
|
+
|
|
679
|
+
if hasattr(self, 'settings_window'):
|
|
680
|
+
self.settings_window.present()
|
|
681
|
+
else:
|
|
682
|
+
self.settings_window = SettingsWindow(self,**kwargs)
|
|
683
|
+
self.settings_window.show_all()
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def open_task_window(self, w = None, data = None):
|
|
687
|
+
self.taskwindow = TaskWindow(self, data)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def open_new_task_dialog(self, w = None, passed_data = None):
|
|
691
|
+
|
|
692
|
+
if hasattr(self, 'new_task_dialog'):
|
|
693
|
+
self.new_task_dialog.present()
|
|
694
|
+
else:
|
|
695
|
+
self.new_task_dialog = NewTaskWDialog(self,passed_data)
|
|
696
|
+
self.new_task_dialog.show_all()
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def signal_handler(self, sig, frame):
|
|
700
|
+
dbg('Signal received',sig,s='signals')
|
|
701
|
+
|
|
702
|
+
if sig == signal.SIGUSR1:
|
|
703
|
+
# try:
|
|
704
|
+
# print('TaskWindow._instance',TaskWindow._instance)
|
|
705
|
+
# if TaskWindow._instance:
|
|
706
|
+
# self.taskwindow.destroy()
|
|
707
|
+
# else:
|
|
708
|
+
# self.open_task_window()
|
|
709
|
+
|
|
710
|
+
# except AttributeError:
|
|
711
|
+
self.open_task_window()
|
|
712
|
+
|
|
713
|
+
elif sig == signal.SIGUSR2:
|
|
714
|
+
self.open_session_options_dialog()
|
|
715
|
+
|
|
716
|
+
else:
|
|
717
|
+
dbg("no handler for received signal",s='signals',l=3)
|
|
718
|
+
|
|
719
|
+
def check_pipe(self):
|
|
720
|
+
print("Listening to pipe at ",pipe_file)
|
|
721
|
+
try:
|
|
722
|
+
with open(pipe_file, "r") as pipe:
|
|
723
|
+
data = pipe.read().strip()
|
|
724
|
+
|
|
725
|
+
print("check_pipe ")
|
|
726
|
+
print(data)
|
|
727
|
+
|
|
728
|
+
pipe.close()
|
|
729
|
+
|
|
730
|
+
# TODO: add registry of special commands
|
|
731
|
+
# if data in ['quit','open_task_window']:
|
|
732
|
+
# TODO: how to all dynamic function name here?
|
|
733
|
+
# GLib.idle_add(self.open_task_window)
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
if data == 'quit':
|
|
737
|
+
GLib.idle_add(self.quit)
|
|
738
|
+
|
|
739
|
+
elif data == 'open_task_window':
|
|
740
|
+
GLib.idle_add(self.open_task_window)
|
|
741
|
+
|
|
742
|
+
elif data == 'stop':
|
|
743
|
+
GLib.idle_add(self.stop_task)
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
else:
|
|
747
|
+
search_results = utils.taskindex_search('"'+str(data)+'"')
|
|
748
|
+
if len(search_results) > 0:
|
|
749
|
+
self.start_task(None, utils.first(search_results)['id'])
|
|
750
|
+
|
|
751
|
+
else:
|
|
752
|
+
error_notice('Commandline Task failed','Could not find a task matching argument '+str(data))
|
|
753
|
+
|
|
754
|
+
except FileNotFoundError as e:
|
|
755
|
+
print("Error starting pip listener, CLI will not work",e)
|
|
756
|
+
|
|
757
|
+
# keep listening
|
|
758
|
+
self.check_pipe()
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def startup():
|
|
762
|
+
# print("startup ",sys.argv)
|
|
763
|
+
|
|
764
|
+
''' if called with no arguments send shutdown to pipe, wait 2 seconds delete pipe file and launch application. if called with arguments send args to pipe if it exist otherwise '''
|
|
765
|
+
|
|
766
|
+
parser = argparse.ArgumentParser(
|
|
767
|
+
# prog='ProgramName',
|
|
768
|
+
# description='What the program does',
|
|
769
|
+
# epilog='Text at the bottom of help'
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
parser.add_argument('task',nargs='?') # optional positional argument
|
|
773
|
+
parser.add_argument('-s', '--debug_systems', nargs="*", default=[]) # option that takes a value
|
|
774
|
+
parser.add_argument('-l', '--debug_level', default=1) # option that takes a value
|
|
775
|
+
parser.add_argument('-f', '--force', action='store_true', help="Force restart by deleting existing named pipe") # on/off flag
|
|
776
|
+
# parser.add_argument('-v', '--verbose', action='store_true') # on/off flag
|
|
777
|
+
|
|
778
|
+
args = parser.parse_args()
|
|
779
|
+
dbg(args)
|
|
780
|
+
|
|
781
|
+
conf.debug_level = int(args.debug_level)
|
|
782
|
+
conf.debug_systems = args.debug_systems
|
|
783
|
+
|
|
784
|
+
if args.force:
|
|
785
|
+
print("Lanched with --force flag, forcibly deleting old pipe")
|
|
786
|
+
try:
|
|
787
|
+
os.remove(pipe_file)
|
|
788
|
+
except Exception as e:
|
|
789
|
+
print(e)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
try:
|
|
793
|
+
os.mkfifo(pipe_file)
|
|
794
|
+
dbg("Named pipe created successfully!", s="cli")
|
|
795
|
+
|
|
796
|
+
signal.signal(signal.SIGUSR1, Application.signal_handler)
|
|
797
|
+
app = Application()
|
|
798
|
+
|
|
799
|
+
if args.task:
|
|
800
|
+
print("Writing args.task to pipe", args.task)
|
|
801
|
+
with open(pipe_file, "w") as pipeout:
|
|
802
|
+
pipeout.write(args.task)
|
|
803
|
+
pipeout.close()
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
except FileExistsError:
|
|
807
|
+
dbg("Named pipe exists, application must be running (or improperly shut down.) ",s="cli")
|
|
808
|
+
|
|
809
|
+
# if args: pass to pipe and exit
|
|
810
|
+
if args.task:
|
|
811
|
+
pipe_line = args.task
|
|
812
|
+
else:
|
|
813
|
+
#otherwise open task window
|
|
814
|
+
pipe_line = "open_task_window"
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
dbg("Writing arg to pipe ",pipe_line, s="cli")
|
|
818
|
+
|
|
819
|
+
with open(pipe_file, "w") as pipeout:
|
|
820
|
+
pipeout.write(pipe_line)
|
|
821
|
+
pipeout.close()
|
|
822
|
+
|
|
823
|
+
exit()
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
except Exception as e:
|
|
828
|
+
print(f"Named pipe creation failed: {e}")
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
if __name__ == "__main__":
|
|
832
|
+
startup()
|
|
833
|
+
# print("called with garv",sys.argv)
|
|
834
|
+
# signal.signal(signal.SIGUSR1, Application.signal_handler) # works
|
|
835
|
+
# app = Application()
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
|