nowfocus 0.4.0__py3-none-any.whl → 0.4.2__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 CHANGED
@@ -57,6 +57,8 @@ setproctitle.setproctitle(conf.app_name)
57
57
  print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file__)))
58
58
 
59
59
  class Application(Gtk.Application):
60
+ icon_tick_number = 0
61
+
60
62
  def __init__(self, *args, **kwargs):
61
63
  super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
62
64
 
@@ -137,7 +139,6 @@ class Application(Gtk.Application):
137
139
 
138
140
 
139
141
 
140
-
141
142
  def print_time_totals(self = None, widget = None):
142
143
 
143
144
  # SELECT extended_label, (SUM(duration) / 60 ) FROM sessions GROUP BY extended_label
@@ -277,10 +278,7 @@ class Application(Gtk.Application):
277
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()
278
279
 
279
280
  playsound('sound/xylophone-chord.mp3',False)
280
-
281
281
 
282
-
283
- icon_tick_number = 0
284
282
 
285
283
  def tick(self):
286
284
 
@@ -306,19 +304,24 @@ class Application(Gtk.Application):
306
304
 
307
305
  label = self.session['label'] + ": " + sec_to_time(duration)
308
306
 
309
- self.indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
310
-
307
+ icon = 'icon-'+str(self.icon_tick_number)+'.svg'
308
+
311
309
  else:
312
310
 
313
- label = conf.user['default_text']
314
- if duration > 60 and duration % 2:
315
- self.indicator.set_icon_full(os.path.abspath('icon/icon-red.svg'),label)
311
+ label = conf.user['default_text'] +" "+ sec_to_time(duration)
312
+
313
+ if duration > 60 and num_is_multiple_of(duration, 2):
314
+ icon = 'icon-red.svg'
315
+
316
316
  else:
317
- self.indicator.set_icon_full(os.path.abspath('icon/icon-1.svg'),label)
317
+ icon = 'icon-1.svg'
318
318
 
319
319
  # https://lazka.github.io/pgi-docs/#AyatanaAppIndicator3-0.1/classes/Indicator.html#AyatanaAppIndicator3.Indicator.set_label
320
+
321
+ self.indicator.set_icon_full(os.path.abspath('icon/'+icon),label)
320
322
  self.indicator.set_label(label,label)
321
323
 
324
+
322
325
  if num_is_multiple_of(self.icon_tick_number,3):
323
326
  self.refresh_all_changed_todo_files()
324
327
 
@@ -580,7 +583,7 @@ class Application(Gtk.Application):
580
583
 
581
584
  def async_refresh(self, w=None, single_todo = None):
582
585
 
583
- self.indicator.set_label("Refreshing Todolists", "Wide")
586
+ self.indicator.set_label("Refreshing Todolists", "Refreshing Todolists")
584
587
  menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
585
588
  self.menu.append(menu_item)
586
589
  self.menu.show_all()
@@ -9,8 +9,6 @@ import requests
9
9
  import conf
10
10
  import utils
11
11
 
12
- # UPDATE sessions SET task_id = "Vikunja_t" || task_id WHERE todolist = 'Vikunja';
13
- # UPDATE sessions SET parent_id = "Vikunja_l" || parent_id WHERE todolist = 'Vikunja';
14
12
 
15
13
  def vikunja_item_id(id, item_type, user_conf):
16
14
  return user_conf['id'] +'_'+ item_type[0] + str(id)
@@ -20,7 +18,7 @@ def add_new_task(user_conf,parent_list,task_label):
20
18
  headers = {'content-type': 'application/json'}
21
19
  data = {'title': task_label}
22
20
 
23
- utils.dbg('add_new_task() parent_list',parent_list, l=0)
21
+ utils.dbg('add_new_task() parent_list',parent_list, l=-1)
24
22
 
25
23
  response = requests.put(user_conf['url']+"api/v1/projects/"+str(parent_list['data']['id'])+"/tasks",json=data, headers=head)
26
24
 
@@ -104,7 +102,6 @@ def get_todos(user_conf):
104
102
  }
105
103
 
106
104
 
107
- priorities = {'DO NOW':1,'Urgent':2,'high':3}
108
105
  headers = {"Authorization": "Bearer "+user_conf['token']}
109
106
  projects_lists = requests.get(user_conf['url']+"api/v1/projects", headers=headers).json()
110
107
 
@@ -139,6 +136,9 @@ def get_todos(user_conf):
139
136
 
140
137
  lists[id]['data']['accepts_tasks'] = True
141
138
 
139
+ # print(p['title']+' is_archived:',p['is_archived'])
140
+ # BUG: archived list aren't provided (But used to be ?) so sublists end up orphaned and dangerous...
141
+
142
142
  if p['is_archived']:
143
143
  lists[id]['status'] = -1
144
144
  else:
@@ -152,6 +152,10 @@ def get_todos(user_conf):
152
152
  if parent_id in lists:
153
153
  lists[id]['parent_label'] = lists[parent_id]['label']
154
154
 
155
+ if lists[parent_id]['status'] == -1:
156
+ lists[id]['status'] = -1
157
+
158
+
155
159
 
156
160
  # NOTE: Originally tasks where queried per list, however this no longer works well because it requires a 'view' id, which comes wih various un-predictable baked-in filters (like done=true). Using the tasks/all api endpoint avoids this but quickly hits the default maxitemsperpage limit (which could, less obviously, show up in the per list method).
157
161
 
@@ -204,7 +208,6 @@ def get_todos(user_conf):
204
208
  # ?per_page=10000
205
209
 
206
210
 
207
-
208
211
  response = requests.get(user_conf['url']+"api/v1/tasks/all", headers=headers)
209
212
  all_tasks = response.json()
210
213
 
@@ -218,8 +221,6 @@ def get_todos(user_conf):
218
221
  # print(json.dumps(all_tasks, indent=4))
219
222
  utils.dbg("Got ",len(all_tasks),"Vikunja tasks", s='vikunja', l=2 )
220
223
 
221
- utils.dbg("Got ",len(all_tasks),"Vikunja tasks", s='vikunja', l=2 )
222
-
223
224
  for i, itemIter in enumerate(all_tasks):
224
225
 
225
226
  t = all_tasks[i]
@@ -228,7 +229,7 @@ def get_todos(user_conf):
228
229
 
229
230
  # utils.dbg(t, s='vikunja', l=3)
230
231
 
231
- # Limit to asks assigned, created by, or favorated by user
232
+ # Limit to tasks assigned, created by, or favorated by user
232
233
  if user_conf["username"]:
233
234
  if t['is_favorite'] != True:
234
235
 
@@ -264,13 +265,12 @@ def get_todos(user_conf):
264
265
  tasks[id]['status'] = -1
265
266
 
266
267
  except Exception as e:
267
- print("No list found for", t['title']," list_id",list_id)
268
+ utils.dbg("No list found for", t['title']," list_id",list_id, s='vikunja')
268
269
  # raise e
269
270
 
270
271
  if t['done'] == True:
271
272
  tasks[id]['status'] = 0
272
273
 
273
- # Prioritize if "due_date" is soon or passed (and not: "0001-01-01T00:00:00Z")
274
274
  if 'due_date' in t and t['due_date'] != "0001-01-01T00:00:00Z":
275
275
 
276
276
  due_date = datetime.strptime(t['due_date'],'%Y-%m-%dT%H:%M:%SZ')
@@ -282,14 +282,13 @@ def get_todos(user_conf):
282
282
  if due_seconds > -86400:
283
283
  tasks[id]['priority'] = 2
284
284
 
285
- # prioritize favorites
286
- if t['is_favorite'] == True:
285
+ if t['priority'] and t['priority'] > 1:
286
+ # Vikunja priority is 1 = lowest, 5 = highest
287
+ #
288
+ tasks[id]['priority'] = utils.invert_number_scale(utils.clamp(utils.force_number(t['priority']),0,5))
289
+ elif t['is_favorite'] == True:
287
290
  tasks[id]['priority'] = 2
288
- elif t['priority'] in priorities:
289
- print("Priority task", t['title'])
290
- tasks[id]['priority'] = priorities[t['priority']]
291
- # print(tasks[id])
292
-
291
+
293
292
  todos = {'lists':lists,'tasks':tasks}
294
293
  return todos
295
294
 
nowfocus/task_window.py CHANGED
@@ -80,7 +80,6 @@ class TaskWindow(Gtk.Window):
80
80
 
81
81
  self.l_sidebar = Gtk.VBox(spacing=5)
82
82
  self.l_sidebar.get_style_context().add_class("subtle")
83
-
84
83
  self.outer_box.add(self.l_sidebar)
85
84
 
86
85
  self.center_box = Gtk.VBox(spacing=10)
@@ -94,18 +93,11 @@ class TaskWindow(Gtk.Window):
94
93
 
95
94
  self.recreate_header()
96
95
 
97
- self.r_sidebar_outer = Gtk.ScrolledWindow()
98
- self.r_sidebar_outer.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
99
- self.outer_box.add(self.r_sidebar_outer)
100
96
 
101
97
  self.r_sidebar = Gtk.VBox(spacing=5)
102
98
  self.r_sidebar.get_style_context().add_class("subtle")
103
-
104
- self.r_sidebar.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
105
- self.r_sidebar.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK)
106
- self.r_sidebar.connect("enter-notify-event",self.test)
107
-
108
- self.r_sidebar_outer.add(self.r_sidebar)
99
+ self.outer_box.add(self.r_sidebar)
100
+
109
101
 
110
102
  self.tick_timer = GLib.timeout_add_seconds(1, self.tick)
111
103
 
@@ -230,6 +222,7 @@ class TaskWindow(Gtk.Window):
230
222
 
231
223
  self.connect("destroy", self.on_destroy)
232
224
 
225
+
233
226
  def test(self=None, one=None, two=None, **kwargs):
234
227
  print('task_window test')
235
228
  print(one)
@@ -385,24 +378,48 @@ class TaskWindow(Gtk.Window):
385
378
  except Exception:
386
379
  pass
387
380
 
381
+
388
382
  def create_sidebars(self):
389
383
  timeit()
384
+
385
+ self.l_sidebar.foreach(lambda child: child.destroy())
386
+ self.r_sidebar.foreach(lambda child: child.destroy())
387
+
390
388
  if conf.user['open_task_window_fullscreen'] and conf.user['show_task_window_sidebars']:
391
- self.r_sidebar_outer.set_size_request(350, -1)
392
389
  self.l_sidebar.set_size_request(350, -1)
390
+ self.r_sidebar.set_size_request(350, -1)
393
391
 
394
- self.SessionEditDialog = SessionEditDialog # passed to show_sessions
392
+ focus_percent_label = Gtk.Label()
393
+ focus_percent_label.set_markup("<b>Focus: "+str(utils.get_percent_time_focused())+"%</b>")
394
+ self.r_sidebar.pack_start(focus_percent_label, False, True, 25)
395
395
 
396
- #show lists on left
397
- self.l_sidebar.foreach(lambda child: child.destroy())
396
+ self.sessions_scrolledwindow = Gtk.ScrolledWindow()
397
+ self.sessions_scrolledwindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
398
+ self.r_sidebar.pack_start(self.sessions_scrolledwindow, True, True, 0)
399
+
400
+ self.sessions_box = Gtk.VBox(spacing=5)
401
+ # self.sessions_box.get_style_context().add_class("subtle")
402
+ self.sessions_scrolledwindow.add(self.sessions_box)
403
+
404
+ # self.r_sidebar.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
405
+ # self.r_sidebar.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK)
406
+ # self.r_sidebar.connect("enter-notify-event",self.test)
407
+
398
408
 
399
409
  self.l_sidebar.add(choose_from_lists(self.select_list_callback, 'None', None, False))
400
410
 
401
- #show sessions on right
402
- show_sessions(None, self, self.r_sidebar, None, 'start_time', '30',None, 30)
411
+ self.SessionEditDialog = SessionEditDialog # passed to show_sessions
412
+ show_sessions(None, self, self.sessions_box, None, 'start_time', '30',None, 30)
413
+
403
414
  self.l_sidebar.show_all()
404
415
  self.r_sidebar.show_all()
405
- timeit()
416
+ else:
417
+ self.r_sidebar.set_size_request(-1, -1)
418
+ self.l_sidebar.set_size_request(-1, -1)
419
+
420
+ self.l_sidebar.hide()
421
+ self.r_sidebar.hide()
422
+ timeit()
406
423
 
407
424
 
408
425
  def on_notes_changed(self,buffer):
@@ -628,11 +645,14 @@ class TaskWindow(Gtk.Window):
628
645
  self.unfullscreen()
629
646
  self.get_style_context().remove_class("large")
630
647
  conf.user['open_task_window_fullscreen'] = False
648
+ self.create_sidebars()
631
649
 
632
650
  else:
633
651
  self.fullscreen()
634
652
  self.get_style_context().add_class("large")
635
653
  conf.user['open_task_window_fullscreen'] = True
654
+ self.create_sidebars()
655
+
636
656
 
637
657
  utils.save_user_settings()
638
658
 
nowfocus/utils.py CHANGED
@@ -464,7 +464,7 @@ def reindex(t=None):
464
464
 
465
465
  def reindex_all():
466
466
 
467
- db_query("DROP TABLE taskindex",None,None,0)
467
+ db_query("DROP TABLE IF EXISTS taskindex",None,None,0)
468
468
  db_query("CREATE VIRTUAL TABLE taskindex USING fts5(id, extended_label, priority, status)")
469
469
  db_query("INSERT INTO taskindex(id, extended_label, priority, status) SELECT id, extended_label, priority, status FROM tasks WHERE tasks.status IS NOT '-1' ")
470
470
 
@@ -903,6 +903,19 @@ def get_total_time(id, category = 'tasks', start_time = None, end_time = None, g
903
903
  return result
904
904
 
905
905
 
906
+ def get_percent_time_focused():
907
+
908
+ rand_seconds = db_query("SELECT SUM(duration) as seconds FROM sessions WHERE sessions.task_id = 'Randomness' AND "+ sessions_timeframe_sql())[0]['seconds']
909
+
910
+ focus_seconds = db_query("SELECT SUM(duration) as seconds FROM sessions WHERE sessions.task_id != 'Randomness' AND "+ sessions_timeframe_sql())[0]['seconds']
911
+
912
+ percent = round(divide(focus_seconds,(focus_seconds + rand_seconds)) * 100, 2)
913
+
914
+ dbg('get_percent_time_focused', str(percent)+"%", 'focus_seconds', focus_seconds, 'rand_seconds', rand_seconds)
915
+
916
+ return percent
917
+
918
+
906
919
  def get_recent_tasks(count = 15):
907
920
  data = db_query("SELECT DISTINCT tasks.* FROM tasks JOIN sessions ON sessions.task_id = tasks.id WHERE tasks.status = 1 GROUP BY tasks.id ORDER BY MAX(sessions.start_time) DESC LIMIT ?",(count,))
908
921
  o = {}
@@ -964,6 +977,25 @@ def force_number(i):
964
977
  return o
965
978
 
966
979
 
980
+ def divide(a, b):
981
+ a = force_number(a)
982
+ b = force_number(b)
983
+
984
+ if a == 0 or b == 0:
985
+ return 0
986
+ else:
987
+ return (a / b)
988
+
989
+ def clamp(value, min_limit, max_limit):
990
+ return max(min_limit, min(value, max_limit))
991
+
992
+
993
+ def invert_number_scale(i, max=5):
994
+ ''' Flips a positive number within a given range '''
995
+ o = abs(i - max )
996
+ return o
997
+
998
+
967
999
  def num_is_multiple_of(i,devisor = 2):
968
1000
  try:
969
1001
  i = int(i)
@@ -1157,7 +1189,11 @@ def choose_from_lists(callback, selected_list_id = None, session = None, accepts
1157
1189
  # new buttongroup for new todolist
1158
1190
  todolist = l['todolist']
1159
1191
  l['header'] = Gtk.Label()
1160
- l['header'].set_markup("<b>"+conf.user['todolists'][todolist]["label"]+"</b>")
1192
+ try:
1193
+ l['header'].set_markup("<b>"+conf.user['todolists'][todolist]["label"]+"</b>")
1194
+ except Exception as e:
1195
+ dbg('Exception creating button group header for list',l, e=e, l=0)
1196
+
1161
1197
  box.add(l['header'])
1162
1198
 
1163
1199
  if l['id'] == selected_list_id:
@@ -1216,7 +1252,7 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
1216
1252
  # separator after priority tasks
1217
1253
  target_menu.prepend(Gtk.SeparatorMenuItem.new())
1218
1254
 
1219
- # Create list sub menus
1255
+ # Create menu for each list
1220
1256
  for list_id, l in lists.items():
1221
1257
  if l['status'] != 1:
1222
1258
  continue
@@ -1228,19 +1264,25 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
1228
1264
  list_menu_items[list_id] = Gtk.MenuItem.new_with_label(lists[list_id]['label']) # the "item" that gets added
1229
1265
  list_menu_items[list_id].set_submenu(list_menus[list_id])
1230
1266
 
1231
- # Add sub menus (items) to parents (sub_menus)
1267
+ # Add each list to parents (sub_menus)
1232
1268
  for list_id, l in lists.items():
1233
1269
  if l['status'] == 1:
1234
1270
 
1235
- if not toplevel_todos and l['id'] == l['todolist']:
1236
- continue
1271
+ try:
1272
+
1273
+ if not toplevel_todos and l['id'] == l['todolist']:
1274
+ continue
1275
+
1276
+ if 'priority' in l and l['priority']:
1277
+ target_menu.prepend(list_menu_items[list_id])
1278
+ elif (toplevel_todos and l['id'] == l['todolist']) or (not toplevel_todos and l['parent_id'] == l['todolist']):
1279
+ target_menu.append(list_menu_items[list_id])
1280
+ else:
1281
+ list_menus[l['parent_id']].append(list_menu_items[list_id])
1237
1282
 
1238
- if 'priority' in l and l['priority']:
1239
- target_menu.prepend(list_menu_items[list_id])
1240
- elif (toplevel_todos and l['id'] == l['todolist']) or (not toplevel_todos and l['parent_id'] == l['todolist']):
1241
- target_menu.append(list_menu_items[list_id])
1242
- else:
1243
- list_menus[l['parent_id']].append(list_menu_items[list_id])
1283
+ except Exception as e:
1284
+ dbg('Error adding list to menu',l,e=e,l=0)
1285
+
1244
1286
 
1245
1287
 
1246
1288
  for id, t in tasks.items():
@@ -1288,27 +1330,29 @@ def add_todos_to_menu(target_menu = None, menu_tasks = None, list_menus = None,
1288
1330
 
1289
1331
  target_menu.show_all()
1290
1332
 
1333
+
1291
1334
  def datetime_minus_calendar_unit(unit = 'days', num = 1, ref_date = None):
1292
1335
  ''' returns a datetime for the start of the unit num units ago (from the optional ref_date)
1293
1336
  unit can be days, weeks, months, or years (plural or singular)
1294
-
1295
1337
  '''
1296
- # Looks weird but...
1338
+
1297
1339
  num = num - 1
1340
+
1298
1341
  if not ref_date:
1299
1342
  ref_date = start_of_day()
1343
+
1300
1344
  if unit in ['day','days']:
1301
1345
  o = ref_date - timedelta(days=num)
1302
- if num > 0: o = o - timedelta(days=num)
1303
- if unit in ['week','weeks']:
1346
+ elif unit in ['week','weeks']:
1304
1347
  o = ref_date - timedelta(days=ref_date.weekday())
1305
1348
  if num > 0: o = o + relativedelta(dt1= o, weeks= - num)
1306
- if unit in ['month','months']:
1349
+ elif unit in ['month','months']:
1307
1350
  o = ref_date.replace(day=1)
1308
1351
  if num > 0: o = o + relativedelta(dt1= o, months= - num)
1309
- if unit in ['year','years']:
1352
+ elif unit in ['year','years']:
1310
1353
  o = ref_date.replace(month=1,day=1)
1311
1354
  if num > 0: o = o + relativedelta(dt1= o, years= - num)
1355
+
1312
1356
  return o
1313
1357
 
1314
1358
  def get_time_target_data(i, item_type = 'tasks'):
@@ -1359,11 +1403,11 @@ def hours_search_timeframes(frame_name = None):
1359
1403
  ''' returns a tuple with start and end time for given frame (if one is provided) or a dict of frame_options.
1360
1404
  '''
1361
1405
 
1362
- default = "this year"
1406
+ default = "all time"
1363
1407
 
1364
1408
  frames = {
1365
1409
  'today':(datetime_minus_calendar_unit('day',1),now()),
1366
- 'yesterday':(datetime_minus_calendar_unit('day',1)),
1410
+ 'yesterday':(datetime_minus_calendar_unit('day',2),datetime_minus_calendar_unit('day',1)),
1367
1411
  'this week':(datetime_minus_calendar_unit('week',1),now()),
1368
1412
  'last week':(datetime_minus_calendar_unit('week',2),datetime_minus_calendar_unit('week',1)),
1369
1413
  '7 days':(datetime_minus_calendar_unit('days',7),now()),
@@ -1392,4 +1436,6 @@ def sessions_timeframe_sql():
1392
1436
  timeframe = hours_search_timeframes(conf.user['hours_search_timeframe'])
1393
1437
 
1394
1438
  timeframe_sql = " sessions.start_time > '"+timeframe[0].strftime("%Y-%m-%d %H:%M:%S")+"' AND sessions.start_time < '"+timeframe[1].strftime("%Y-%m-%d %H:%M:%S")+"' "
1439
+
1440
+ # dbg('sessions_timeframe_sql',timeframe_sql,s='search')
1395
1441
  return timeframe_sql
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nowfocus
3
- Version: 0.4.0
3
+ Version: 0.4.2
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
7
+ Project-URL: repository, https://codeberg.org/AltruistEnterprises/nowfocus
7
8
  Classifier: Programming Language :: Python :: 3
8
9
  Classifier: Operating System :: OS Independent
9
10
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
@@ -40,12 +41,9 @@ Requires-Dist: vobject
40
41
  Requires-Dist: x-wr-timezone
41
42
  Dynamic: license-file
42
43
 
43
- ---
44
- mainfont: sans-serif
45
- ---
46
- <div align="center"><img src="https://gitlab.com/GitFr33/nowfocus/-/raw/main/nowfocus.svg" width="60" align="center">
44
+ <div align="center"><img src="https://www.nowfocus.org/android-chrome-192x192.png" width="60" align="center">
47
45
 
48
- # *nowfocus* <br> Open-source task timer for Linux
46
+ # <a href="https://www.nowfocus.org/">*Nowfocus*</a> <br> Open-source task timer for Linux
49
47
 
50
48
  **Avoid multifailing. Master your to-do lists. Track your time.**
51
49
 
@@ -100,20 +98,16 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
100
98
 
101
99
  ## Installation
102
100
 
103
- <!-- no longer needed: meson libdbus-glib-1-dev patchelf -->
104
101
  <!-- # note: gir1.2-appindicator3-0.1 can be substituted for gir1.2-ayatanaappindicator3-0.1 -->
105
102
 
106
103
  1. Run the following in terminal to install and setup:
107
104
  ```
108
105
  # Install dependencies
109
- sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev
110
-
111
- # only needed X display (rather than wayland)
112
- sudo apt install xprintidle
106
+ sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev xprintidle
113
107
 
114
108
  # Set up pipx
115
109
  pipx ensurepath
116
- source ~.bashrc
110
+ source ~/.bashrc
117
111
 
118
112
  # Install nowfocus
119
113
  pipx install nowfocus
@@ -174,15 +168,22 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
174
168
  - Start with verbose logging use: `nowfocus -l 3`
175
169
  - Start with targeted verbose logging use: `nowfocus -s trello`
176
170
 
177
- <!--
178
- TODO: test that this works!
179
- ## Development
180
171
 
181
- ### Install from Source
172
+ ## Reporting Issues
173
+ [Open an issue on Codeberg](https://codeberg.org/AltruistEnterprises/nowfocus/issues) (Please include as much detail as you can.)
182
174
 
183
- - Clone this repo somewhere (referred to as `YOUR_INSTALL_PATH`)
184
- - Change to `YOUR_INSTALL_PATH` directory with `cd YOUR_INSTALL_PATH/nowfocus`
185
- - build python module with `python3 -m build` (this should be done in a venv and will require some dependecies...)
186
- - pipx install -e --force YOUR_INSTALL_PATH/monotask/ -->
187
175
 
176
+ ## Development
177
+ [Fork **nowfocus** source code on Codeberg (GPL)](https://codeberg.org/AltruistEnterprises/nowfocus/issues)
178
+
179
+ ### Install From Source
180
+ ```
181
+ git clone https://codeberg.org/AltruistEnterprises/nowfocus/nowfocus.git
182
+ cd nowfocus
183
+ python3 -m venv .venv/nowfocus-build
184
+ source .venv/nowfocus-build/bin/activate
185
+ pip install -r build-requirements.txt
186
+ python3 -m build
187
+ pipx install -e --force YOUR_INSTALL_PATH
188
+ ```
188
189
  <!--built with python + GTK -->
@@ -1,5 +1,5 @@
1
1
  nowfocus/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- nowfocus/__main__.py,sha256=HHiNH7qMwTnsipWhRHSAbEWuu0O7f1ZB1z2dyj_ghBw,32424
2
+ nowfocus/__main__.py,sha256=UlZn9O7eSKJRDxG5GrYSfBmRAKX0yweq80sxz59pIXU,32401
3
3
  nowfocus/conf.py,sha256=5YLSw3ZipFyq62rOQYruIo_yCPn_f8YoyLHWpUYiE3g,6942
4
4
  nowfocus/example-todo.txt,sha256=HRaNcPB1k8cksTtJS1GPqMjOdOY3gAUTWiSL0_ip0q8,265
5
5
  nowfocus/new_task_dialog.py,sha256=GG49tOAwXiUKAHeaKnCG8Q3SZlL5pJijRZEcSjMwQng,4350
@@ -8,9 +8,9 @@ nowfocus/session_options.py,sha256=QVwJA53U7qZsbLe-OFr6UuFeaquco_yps_CRXsQ2_q4,5
8
8
  nowfocus/sessions.csv,sha256=kYpr06yQg_J86NQ4AiYw4RnQchcw3ouPKVYa1lYDUNo,39
9
9
  nowfocus/settings.py,sha256=Ufabuqd90tTDlzXB68jsbDz7233CEMsjnmPzaTSEHLI,35150
10
10
  nowfocus/styles.css,sha256=PG1SrLkwSSay8M2VKeRcE0UdK54ndsEDFnRLRkmP-9M,510
11
- nowfocus/task_window.py,sha256=QynIGDnJiBiKLKO1yWJx6MIfRN3gGoGkSt4R_Tp6ApA,27611
11
+ nowfocus/task_window.py,sha256=o9O0vv-uTvNO6pelyJ8YRgMeJsiYQd_zqm6-9JMzjBY,28367
12
12
  nowfocus/user_idle_time.py,sha256=kPZ_bhoBdVMIBJXn2602FUfS4r-u8FnTq5Ze9D5QfHE,2363
13
- nowfocus/utils.py,sha256=IfjpqguMbLg3G6JxtLZbSDvyCGXdNmnO83zqEl1jw4w,48482
13
+ nowfocus/utils.py,sha256=fkixBjP3_vpxDENoRQcOL1LUDYJEONlZvoXeU4qSqZE,49784
14
14
  nowfocus/connectors/activitywatch.py,sha256=QbkOmjIOiVwccWc2xhhePd0Abww5vEiVpCNjeqOyYGg,921
15
15
  nowfocus/connectors/caldav.py,sha256=PeM_9yJC8W17L8Y5AyS75o6GfzTrPoMYKIvetND8T78,5089
16
16
  nowfocus/connectors/csv.py,sha256=FwMpHM5lPIT90HKBCQUncpaW7zqFjlHjMwKR0-XWg-4,821
@@ -21,7 +21,7 @@ nowfocus/connectors/todo_template.py,sha256=R37fA2LXo8_LpWIgqozytI5RqIUjGggFHup2
21
21
  nowfocus/connectors/todotxt.py,sha256=QCZjbIhY4Lm37YD0GsKJQUqbj7s3eYmZGgRwUCndZ5w,3857
22
22
  nowfocus/connectors/trello.py,sha256=VqwnvHGXXcljmdf6kRZcE6sfeBQYhped_KVBEBOzWXM,6072
23
23
  nowfocus/connectors/txt.py,sha256=iskJsw3dZnI4bIeEDtZCY-aQfKRKtoGATEJ0k13npxI,8125
24
- nowfocus/connectors/vikunja.py,sha256=jOEQGGMu9tJJXlcJwtbsqLkCTqbsSlnja-QoyBrJ9eM,10856
24
+ nowfocus/connectors/vikunja.py,sha256=Lg1lgF3C5B606CUf3-U8pVtPt7Cp6IKkT5wFN779V8w,10799
25
25
  nowfocus/desktop-extras/nowfocus.desktop,sha256=0kWsx0ZfvPbubGG1uuFSHxxYUw2GV9Ly_rtlboM1mak,294
26
26
  nowfocus/desktop-extras/nowfocus.png,sha256=P5rn6-0EAJa2WXf4SJoaNtLRUfiV3LdsOroPKsR6GfA,15148
27
27
  nowfocus/desktop-extras/nowfocus.svg,sha256=nps7naZzuhWWuKzQbpvxr9wLyzjmzMPzNHSBQMVetOo,2137
@@ -50,9 +50,9 @@ nowfocus/icon/settings.svg,sha256=fgkGJouPPtZLxZn2nr_5pEp9MdhRSRaW9mtdxhJHDuQ,39
50
50
  nowfocus/sound/bell-xylophone-g.mp3,sha256=1OBcRWvD87AGNcq1uZFR8HqG0nanJykImERfVDVxHD4,53891
51
51
  nowfocus/sound/dinner-bell.mp3,sha256=hjjO0xqA4uXpYw9KLwwlBnrVfRhVq1K5OXzwlMXhRn4,113620
52
52
  nowfocus/sound/xylophone-chord.mp3,sha256=gwgBSqhMt5PMzT5N03Z6TvDgipQZfnkEz_o81Rq5Z1U,131806
53
- nowfocus-0.4.0.dist-info/licenses/LICENSE,sha256=fSJzoHs1EOCwEd7FIyokFeGEma7NKmTVEdHkCr5OIV4,35127
54
- nowfocus-0.4.0.dist-info/METADATA,sha256=uXBd4bD5KcsgN-0GOlZ_xsh-_iSYZys6O5oRrXkrAXo,7025
55
- nowfocus-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
- nowfocus-0.4.0.dist-info/entry_points.txt,sha256=RbYY19-irSoNVglNeNnL9D36cHft7aKsaEGEYoSH3pA,51
57
- nowfocus-0.4.0.dist-info/top_level.txt,sha256=3uLd9BwmfarZwqVUxkSJuVwJ8qHzjThte8rt_UYG7tE,9
58
- nowfocus-0.4.0.dist-info/RECORD,,
53
+ nowfocus-0.4.2.dist-info/licenses/LICENSE,sha256=fSJzoHs1EOCwEd7FIyokFeGEma7NKmTVEdHkCr5OIV4,35127
54
+ nowfocus-0.4.2.dist-info/METADATA,sha256=MUKTtnWDa08P4w_Z0FmW3QvI9-Q6aA11bmNYV-duISU,7141
55
+ nowfocus-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ nowfocus-0.4.2.dist-info/entry_points.txt,sha256=RbYY19-irSoNVglNeNnL9D36cHft7aKsaEGEYoSH3pA,51
57
+ nowfocus-0.4.2.dist-info/top_level.txt,sha256=3uLd9BwmfarZwqVUxkSJuVwJ8qHzjThte8rt_UYG7tE,9
58
+ nowfocus-0.4.2.dist-info/RECORD,,