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