nowfocus 0.2.13__tar.gz → 0.4.2__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.2}/PKG-INFO +26 -24
- {nowfocus-0.2.13 → nowfocus-0.4.2}/README.md +21 -15
- {nowfocus-0.2.13 → nowfocus-0.4.2}/pyproject.toml +12 -9
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/__main__.py +89 -83
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/conf.py +4 -5
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/todotxt.py +8 -3
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/txt.py +14 -2
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/vikunja.py +16 -17
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/new_task_dialog.py +3 -4
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/settings.py +10 -51
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/styles.css +9 -11
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/task_window.py +125 -66
- nowfocus-0.4.2/src/nowfocus/user_idle_time.py +82 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/utils.py +231 -127
- {nowfocus-0.2.13 → nowfocus-0.4.2/src/nowfocus.egg-info}/PKG-INFO +26 -24
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus.egg-info/SOURCES.txt +1 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus.egg-info/requires.txt +3 -6
- {nowfocus-0.2.13 → nowfocus-0.4.2}/LICENSE +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/setup.cfg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/__init__.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/activitywatch.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/caldav.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/csv.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/psc_timetracker.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/taskwarrior.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/timewarrior.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/todo_template.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/connectors/trello.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/desktop-extras/nowfocus.desktop +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/desktop-extras/nowfocus.png +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/desktop-extras/nowfocus.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/example-todo.txt +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/cancel.png +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/cancel.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/edit.png +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/edit.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-0.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-1.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-2.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-3.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-4.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-5.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-6.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-7.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-8.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-9.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon-red.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/icon.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/mark-done.png +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/mark-done.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/pause.png +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/pause.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/settings.png +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/icon/settings.svg +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/session_edit_dialog.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/session_options.py +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/sessions.csv +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/sound/bell-xylophone-g.mp3 +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/sound/dinner-bell.mp3 +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus/sound/xylophone-chord.mp3 +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus.egg-info/dependency_links.txt +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus.egg-info/entry_points.txt +0 -0
- {nowfocus-0.2.13 → nowfocus-0.4.2}/src/nowfocus.egg-info/top_level.txt +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nowfocus
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.4.2
|
|
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
|
+
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+)
|
|
@@ -14,17 +15,13 @@ License-File: LICENSE
|
|
|
14
15
|
Requires-Dist: psutil
|
|
15
16
|
Requires-Dist: pygobject==3.50.0
|
|
16
17
|
Requires-Dist: pycairo==1.27
|
|
17
|
-
Requires-Dist: meson
|
|
18
|
-
Requires-Dist: meson-python
|
|
19
18
|
Requires-Dist: attrs
|
|
20
19
|
Requires-Dist: caldav
|
|
21
20
|
Requires-Dist: certifi
|
|
22
21
|
Requires-Dist: charset-normalizer
|
|
23
|
-
Requires-Dist: dbus-idle
|
|
24
|
-
Requires-Dist: dbus-python
|
|
25
22
|
Requires-Dist: icalendar
|
|
26
23
|
Requires-Dist: idna
|
|
27
|
-
Requires-Dist:
|
|
24
|
+
Requires-Dist: pywin32>=221; platform_system == "Windows"
|
|
28
25
|
Requires-Dist: kitchen
|
|
29
26
|
Requires-Dist: lxml
|
|
30
27
|
Requires-Dist: playsound3
|
|
@@ -42,12 +39,11 @@ Requires-Dist: tzlocal
|
|
|
42
39
|
Requires-Dist: urllib3
|
|
43
40
|
Requires-Dist: vobject
|
|
44
41
|
Requires-Dist: x-wr-timezone
|
|
45
|
-
Requires-Dist: watchdog
|
|
46
42
|
Dynamic: license-file
|
|
47
43
|
|
|
48
|
-
<div align="center"><img src="https://
|
|
44
|
+
<div align="center"><img src="https://www.nowfocus.org/android-chrome-192x192.png" width="60" align="center">
|
|
49
45
|
|
|
50
|
-
#
|
|
46
|
+
# <a href="https://www.nowfocus.org/">*Nowfocus*</a> <br> Open-source task timer for Linux
|
|
51
47
|
|
|
52
48
|
**Avoid multifailing. Master your to-do lists. Track your time.**
|
|
53
49
|
|
|
@@ -102,17 +98,16 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
|
|
|
102
98
|
|
|
103
99
|
## Installation
|
|
104
100
|
|
|
101
|
+
<!-- # note: gir1.2-appindicator3-0.1 can be substituted for gir1.2-ayatanaappindicator3-0.1 -->
|
|
102
|
+
|
|
105
103
|
1. Run the following in terminal to install and setup:
|
|
106
104
|
```
|
|
107
105
|
# Install dependencies
|
|
108
|
-
sudo apt install pipx gir1.2-
|
|
109
|
-
|
|
110
|
-
# note gir1.2-ayatanaappindicator3-0.1 can be substituted for gir1.2-appindicator3-0.1
|
|
106
|
+
sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev xprintidle
|
|
111
107
|
|
|
112
108
|
# Set up pipx
|
|
113
109
|
pipx ensurepath
|
|
114
|
-
|
|
115
|
-
# At this point you may need to source your .bashrc (or open a new terminal)
|
|
110
|
+
source ~/.bashrc
|
|
116
111
|
|
|
117
112
|
# Install nowfocus
|
|
118
113
|
pipx install nowfocus
|
|
@@ -174,14 +169,21 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
|
|
|
174
169
|
- Start with targeted verbose logging use: `nowfocus -s trello`
|
|
175
170
|
|
|
176
171
|
|
|
177
|
-
##
|
|
178
|
-
|
|
179
|
-
### 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.)
|
|
180
174
|
|
|
181
|
-
- Install dependencies from above.
|
|
182
|
-
- Clone this repo somewhere (referred to as `YOUR_INSTALL_PATH`)
|
|
183
|
-
- Change to `YOUR_INSTALL_PATH` directory with `cd YOUR_INSTALL_PATH/nowfocus`
|
|
184
|
-
- 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
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
|
+
```
|
|
187
189
|
<!--built with python + GTK -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<div align="center"><img src="https://
|
|
1
|
+
<div align="center"><img src="https://www.nowfocus.org/android-chrome-192x192.png" width="60" align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# <a href="https://www.nowfocus.org/">*Nowfocus*</a> <br> Open-source task timer for Linux
|
|
4
4
|
|
|
5
5
|
**Avoid multifailing. Master your to-do lists. Track your time.**
|
|
6
6
|
|
|
@@ -55,17 +55,16 @@ nowfocus is a clean, keyboard-driven time management dashboard that flexibly con
|
|
|
55
55
|
|
|
56
56
|
## Installation
|
|
57
57
|
|
|
58
|
+
<!-- # note: gir1.2-appindicator3-0.1 can be substituted for gir1.2-ayatanaappindicator3-0.1 -->
|
|
59
|
+
|
|
58
60
|
1. Run the following in terminal to install and setup:
|
|
59
61
|
```
|
|
60
62
|
# Install dependencies
|
|
61
|
-
sudo apt install pipx gir1.2-
|
|
62
|
-
|
|
63
|
-
# note gir1.2-ayatanaappindicator3-0.1 can be substituted for gir1.2-appindicator3-0.1
|
|
63
|
+
sudo apt install pipx gir1.2-ayatanaappindicator3-0.1 libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev xprintidle
|
|
64
64
|
|
|
65
65
|
# Set up pipx
|
|
66
66
|
pipx ensurepath
|
|
67
|
-
|
|
68
|
-
# At this point you may need to source your .bashrc (or open a new terminal)
|
|
67
|
+
source ~/.bashrc
|
|
69
68
|
|
|
70
69
|
# Install nowfocus
|
|
71
70
|
pipx install nowfocus
|
|
@@ -127,14 +126,21 @@ Open nowfocus **Settings** from the indicator menu or tasks window and connect y
|
|
|
127
126
|
- Start with targeted verbose logging use: `nowfocus -s trello`
|
|
128
127
|
|
|
129
128
|
|
|
130
|
-
##
|
|
131
|
-
|
|
132
|
-
### Install from Source
|
|
129
|
+
## Reporting Issues
|
|
130
|
+
[Open an issue on Codeberg](https://codeberg.org/AltruistEnterprises/nowfocus/issues) (Please include as much detail as you can.)
|
|
133
131
|
|
|
134
|
-
- Install dependencies from above.
|
|
135
|
-
- Clone this repo somewhere (referred to as `YOUR_INSTALL_PATH`)
|
|
136
|
-
- Change to `YOUR_INSTALL_PATH` directory with `cd YOUR_INSTALL_PATH/nowfocus`
|
|
137
|
-
- 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/
|
|
139
132
|
|
|
133
|
+
## Development
|
|
134
|
+
[Fork **nowfocus** source code on Codeberg (GPL)](https://codeberg.org/AltruistEnterprises/nowfocus/issues)
|
|
135
|
+
|
|
136
|
+
### Install From Source
|
|
137
|
+
```
|
|
138
|
+
git clone https://codeberg.org/AltruistEnterprises/nowfocus/nowfocus.git
|
|
139
|
+
cd nowfocus
|
|
140
|
+
python3 -m venv .venv/nowfocus-build
|
|
141
|
+
source .venv/nowfocus-build/bin/activate
|
|
142
|
+
pip install -r build-requirements.txt
|
|
143
|
+
python3 -m build
|
|
144
|
+
pipx install -e --force YOUR_INSTALL_PATH
|
|
145
|
+
```
|
|
140
146
|
<!--built with python + GTK -->
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nowfocus"
|
|
3
|
-
version = "0.2
|
|
3
|
+
version = "0.4.2"
|
|
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",
|
|
33
|
+
# "jeepney",
|
|
32
34
|
"icalendar",
|
|
33
35
|
"idna",
|
|
34
|
-
"
|
|
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
|
+
repository = "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
|
|
|
@@ -54,20 +57,24 @@ setproctitle.setproctitle(conf.app_name)
|
|
|
54
57
|
print(conf.app_name +" running from " + os.path.dirname(os.path.realpath(__file__)))
|
|
55
58
|
|
|
56
59
|
class Application(Gtk.Application):
|
|
60
|
+
icon_tick_number = 0
|
|
61
|
+
|
|
57
62
|
def __init__(self, *args, **kwargs):
|
|
58
|
-
super().__init__(*args, application_id="org.
|
|
63
|
+
super().__init__(*args, application_id="org.nowfocus.nowfocus", **kwargs)
|
|
64
|
+
|
|
65
|
+
# try:
|
|
66
|
+
# # To put everything here...
|
|
67
|
+
# # this doesn't work because as soon as an exception occurs ii jumps to the handler and breaks all the following code.
|
|
59
68
|
|
|
69
|
+
# except Exception as e:
|
|
70
|
+
# print(conf.app_name +' had a pretty bad error. Please submit the following trace as an issue on the git repo or email it to the developers ')
|
|
71
|
+
# traceback.print_tb(e.__traceback__)
|
|
72
|
+
# return None
|
|
73
|
+
|
|
60
74
|
self.window = None
|
|
61
75
|
|
|
62
76
|
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
|
-
}
|
|
77
|
+
self.session = default_session()
|
|
71
78
|
|
|
72
79
|
self.menu_tasks = {}
|
|
73
80
|
self.list_menus = {}
|
|
@@ -80,7 +87,7 @@ class Application(Gtk.Application):
|
|
|
80
87
|
# menu.set_reserve_toggle_size(False) # skip menu left padding, doesn't work
|
|
81
88
|
|
|
82
89
|
utils.db_init()
|
|
83
|
-
utils.
|
|
90
|
+
utils.db_schema_update()
|
|
84
91
|
|
|
85
92
|
|
|
86
93
|
# self.update_menu()
|
|
@@ -90,7 +97,6 @@ class Application(Gtk.Application):
|
|
|
90
97
|
|
|
91
98
|
self.indicator.set_menu(self.menu)
|
|
92
99
|
|
|
93
|
-
# main_tick_timer = GLib.timeout_add_seconds(conf.user['tick_interval'], self.tick)
|
|
94
100
|
main_tick_timer = GLib.timeout_add_seconds(1, self.tick)
|
|
95
101
|
|
|
96
102
|
try:
|
|
@@ -105,7 +111,7 @@ class Application(Gtk.Application):
|
|
|
105
111
|
except Exception as e:
|
|
106
112
|
dbg("Error resuming session",e,l=1)
|
|
107
113
|
|
|
108
|
-
|
|
114
|
+
self.UserIdleTime = UserIdleTime()
|
|
109
115
|
|
|
110
116
|
self.pipethread = threading.Thread(target=self.check_pipe)
|
|
111
117
|
self.pipethread.daemon = True
|
|
@@ -133,7 +139,6 @@ class Application(Gtk.Application):
|
|
|
133
139
|
|
|
134
140
|
|
|
135
141
|
|
|
136
|
-
|
|
137
142
|
def print_time_totals(self = None, widget = None):
|
|
138
143
|
|
|
139
144
|
# SELECT extended_label, (SUM(duration) / 60 ) FROM sessions GROUP BY extended_label
|
|
@@ -157,39 +162,40 @@ class Application(Gtk.Application):
|
|
|
157
162
|
|
|
158
163
|
def quit(self, widget_or_signal_source=None, condition=None):
|
|
159
164
|
print("Adios ", conf.app_name)
|
|
160
|
-
# print("widget_or_signal_source ", widget_or_signal_source)
|
|
161
|
-
# print("condition ", condition)
|
|
162
165
|
|
|
163
166
|
if self.is_running:
|
|
164
|
-
|
|
167
|
+
dbg('Caching active session', self.session['label'])
|
|
165
168
|
db_set_session_cache(self.session)
|
|
169
|
+
|
|
166
170
|
try:
|
|
167
|
-
# print("before os.remove(conf.pipe)")
|
|
168
171
|
os.remove(conf.pipe)
|
|
169
|
-
print("Pipe removed")
|
|
170
172
|
except Exception as e:
|
|
171
|
-
|
|
173
|
+
dbd("Error removing conf.pipe in quit",e)
|
|
172
174
|
|
|
173
175
|
notify.uninit()
|
|
174
176
|
Gtk.main_quit()
|
|
175
177
|
exit()
|
|
176
178
|
|
|
177
179
|
|
|
178
|
-
def
|
|
180
|
+
def seconds_since_user_active(self):
|
|
179
181
|
# returns seconds of inactivity
|
|
180
|
-
# See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux
|
|
182
|
+
# See https://stackoverflow.com/questions/67083083/how-to-get-idle-time-in-linux or
|
|
181
183
|
|
|
182
184
|
# Works on x11 but not wayland
|
|
183
185
|
# Requires xprintidle (sudo apt install xprintidle)
|
|
184
186
|
# idle_time = int(int(subprocess.getoutput('xprintidle')) / 1000)
|
|
185
187
|
|
|
186
|
-
#
|
|
188
|
+
# Version that works, using: https://github.com/bkbilly/dbus_idle
|
|
189
|
+
# but has many deps
|
|
187
190
|
# Requires:
|
|
188
191
|
# sudo apt install meson libdbus-glib-1-dev patchelf
|
|
189
192
|
# pip install dbus-idle
|
|
190
193
|
|
|
191
|
-
idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
|
|
192
|
-
return idle_time
|
|
194
|
+
# idle_time = int(int(IdleMonitor().get_dbus_idle()) / 1000)
|
|
195
|
+
# return idle_time
|
|
196
|
+
|
|
197
|
+
return self.UserIdleTime.get()
|
|
198
|
+
|
|
193
199
|
|
|
194
200
|
|
|
195
201
|
def toggle_do_not_disturb(self, widget):
|
|
@@ -210,7 +216,7 @@ class Application(Gtk.Application):
|
|
|
210
216
|
if 'do-not-disturb' in self.session:
|
|
211
217
|
return None
|
|
212
218
|
|
|
213
|
-
afk_time = self.
|
|
219
|
+
afk_time = self.seconds_since_user_active()
|
|
214
220
|
# print("Idle time: "+str(afk_time))
|
|
215
221
|
|
|
216
222
|
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 +230,7 @@ class Application(Gtk.Application):
|
|
|
224
230
|
|
|
225
231
|
if(self.is_running == False):
|
|
226
232
|
|
|
227
|
-
if
|
|
233
|
+
if num_is_multiple_of(minutes,conf.user['randomness_interrupt_interval']):
|
|
228
234
|
if afk_time < 30:
|
|
229
235
|
|
|
230
236
|
notify.Notification.new("What Am I doing?","Your randomness timer is at "+str(minutes)+" minutes. ", None).show()
|
|
@@ -243,7 +249,6 @@ class Application(Gtk.Application):
|
|
|
243
249
|
# only show this once
|
|
244
250
|
if afk_time < 181:
|
|
245
251
|
self.open_task_window(None,{'afk_time':afk_time})
|
|
246
|
-
# session_options_dialog(None, 'test input_data')
|
|
247
252
|
else:
|
|
248
253
|
if self.session['label'] in conf.user['custom_pomodoro_intervals']:
|
|
249
254
|
check = conf.user['custom_pomodoro_intervals'][self.session['label']]
|
|
@@ -260,7 +265,7 @@ class Application(Gtk.Application):
|
|
|
260
265
|
t = self.session['target']
|
|
261
266
|
|
|
262
267
|
t['percent'] = round(( (t['starting_value'] + minutes) / t['value'] ) * 100,1)
|
|
263
|
-
print("At
|
|
268
|
+
print("At", t['percent'], "% of ", t['scope'], " target")
|
|
264
269
|
|
|
265
270
|
if t['type'] == 'max':
|
|
266
271
|
if t['percent'] >= 100:
|
|
@@ -273,66 +278,80 @@ class Application(Gtk.Application):
|
|
|
273
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()
|
|
274
279
|
|
|
275
280
|
playsound('sound/xylophone-chord.mp3',False)
|
|
276
|
-
|
|
277
281
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
icon_tick_number = 0
|
|
281
|
-
|
|
282
|
+
|
|
282
283
|
def tick(self):
|
|
283
|
-
menu = self.menu
|
|
284
|
-
indicator = self.indicator
|
|
285
284
|
|
|
286
285
|
# check for suspend indicated by gap in tick intervals
|
|
287
|
-
time_since_last_tick = round(time_difference(self.session['start_time'])
|
|
286
|
+
time_since_last_tick = round(time_difference(self.session['start_time']) - self.session['duration'])
|
|
288
287
|
if time_since_last_tick > 10:
|
|
289
|
-
|
|
290
|
-
|
|
288
|
+
dbg(time_since_last_tick, " seconds since last tick. Probably just woke from suspend. ")
|
|
291
289
|
if self.is_running:
|
|
292
290
|
self.open_task_window(None,{'afk_time':time_since_last_tick})
|
|
293
291
|
else:
|
|
294
|
-
print("resetting randomness timer")
|
|
295
|
-
self.session['duration'] = 0
|
|
296
292
|
self.session['start_time'] = now()
|
|
297
293
|
|
|
298
|
-
|
|
299
|
-
self.session['duration'] = int(time_difference(self.session['start_time']))
|
|
294
|
+
duration = self.session['duration'] = int(time_difference(self.session['start_time']))
|
|
300
295
|
|
|
301
|
-
if
|
|
296
|
+
if num_is_multiple_of(duration,60):
|
|
302
297
|
self.tock()
|
|
303
298
|
|
|
304
|
-
if
|
|
305
|
-
self.icon_tick_number
|
|
299
|
+
if self.is_running == True:
|
|
300
|
+
self.icon_tick_number += 1
|
|
306
301
|
|
|
307
302
|
if self.icon_tick_number > 8:
|
|
308
303
|
self.icon_tick_number = 1
|
|
309
304
|
|
|
310
|
-
label = self.session['label'] + ": " + sec_to_time(
|
|
305
|
+
label = self.session['label'] + ": " + sec_to_time(duration)
|
|
306
|
+
|
|
307
|
+
icon = 'icon-'+str(self.icon_tick_number)+'.svg'
|
|
311
308
|
|
|
312
|
-
indicator.set_icon_full(os.path.abspath('icon/icon-'+str(self.icon_tick_number)+'.svg'),label)
|
|
313
|
-
|
|
314
309
|
else:
|
|
315
310
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if
|
|
319
|
-
|
|
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
|
+
|
|
320
316
|
else:
|
|
321
|
-
|
|
317
|
+
icon = 'icon-1.svg'
|
|
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, "Wide")
|
|
325
320
|
|
|
326
|
-
|
|
327
|
-
|
|
321
|
+
self.indicator.set_icon_full(os.path.abspath('icon/'+icon),label)
|
|
322
|
+
self.indicator.set_label(label,label)
|
|
328
323
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
324
|
+
|
|
325
|
+
if num_is_multiple_of(self.icon_tick_number,3):
|
|
326
|
+
self.refresh_all_changed_todo_files()
|
|
327
|
+
|
|
333
328
|
return True
|
|
334
329
|
|
|
335
330
|
|
|
331
|
+
def refresh_all_changed_todo_files(self):
|
|
332
|
+
for id, todo_config in conf.user['todolists'].items():
|
|
333
|
+
if todo_config['status'] and 'watch_file' in todo_config:
|
|
334
|
+
self.refresh_todo_if_file_changed(todo_config)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def refresh_todo_if_file_changed(self, todo_config):
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
todo_m_time = round(os.stat(todo_config['file']).st_mtime)
|
|
341
|
+
|
|
342
|
+
if todo_config['id'] not in conf.todo_file_change_times:
|
|
343
|
+
conf.todo_file_change_times[todo_config['id']] = todo_m_time
|
|
344
|
+
|
|
345
|
+
elif todo_m_time != conf.todo_file_change_times[todo_config['id']]:
|
|
346
|
+
dbg(todo_config['id']+" file was changed!",s='todoloading')
|
|
347
|
+
self.async_refresh(None,todo_config)
|
|
348
|
+
conf.todo_file_change_times[todo_config['id']] = todo_m_time
|
|
349
|
+
|
|
350
|
+
except Exception as e:
|
|
351
|
+
# TODO: consider how to quietly handle errors when refresh is not prompted by the user and temporarily disabling
|
|
352
|
+
handle_todo_read_error(todo_config,e)
|
|
353
|
+
|
|
354
|
+
|
|
336
355
|
def start_task(self, w = None, task_data_or_id = None, transfer_current_session_time = False):
|
|
337
356
|
|
|
338
357
|
if isinstance(task_data_or_id, dict):
|
|
@@ -356,7 +375,10 @@ class Application(Gtk.Application):
|
|
|
356
375
|
|
|
357
376
|
if(self.is_running == True):
|
|
358
377
|
self.stop_task()
|
|
359
|
-
|
|
378
|
+
elif(self.session['duration'] > 60):
|
|
379
|
+
# Log randomness session (to internal db only) if longer than one minute
|
|
380
|
+
utils.db_save_session(self.session)
|
|
381
|
+
|
|
360
382
|
|
|
361
383
|
self.is_running = True
|
|
362
384
|
|
|
@@ -450,26 +472,20 @@ class Application(Gtk.Application):
|
|
|
450
472
|
try:
|
|
451
473
|
|
|
452
474
|
done_thread = threading.Thread(target=conf.todo_connectors[todolist_conf['type']].mark_task_done, args=(task,) )
|
|
475
|
+
|
|
453
476
|
conf.todo_sync_times[todolist_conf['id']] = now() # this is to avoid causing a refresh, perhaps not the best though
|
|
454
477
|
|
|
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
478
|
done_thread.start()
|
|
462
479
|
|
|
463
480
|
db_query("UPDATE tasks set status = '0' WHERE id = ? ",(task['id'],) )
|
|
464
481
|
utils.reindex_one(task)
|
|
465
482
|
|
|
466
|
-
# print('remove menu item')
|
|
467
483
|
self.menu_tasks[task['id']].destroy()
|
|
468
|
-
|
|
484
|
+
|
|
469
485
|
playsound('sound/xylophone-chord.mp3',False)
|
|
470
486
|
|
|
471
487
|
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 )
|
|
488
|
+
error_notice('Error Marking Task Done'," Marking "+ task['label']+" as done in "+todolist_conf['label']+" had a serious failure", e=e )
|
|
473
489
|
|
|
474
490
|
|
|
475
491
|
|
|
@@ -533,19 +549,12 @@ class Application(Gtk.Application):
|
|
|
533
549
|
self.menu.show_all()
|
|
534
550
|
|
|
535
551
|
self.is_running = False
|
|
536
|
-
# print(utils.get_times(task))
|
|
537
552
|
if action != 'cancel':
|
|
538
553
|
notify.Notification.new("Focused on "+session['label']+" for "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
|
|
539
554
|
# notify.Notification.new(action.capitalize()+" "+session['label']+" "+sec_to_time(session['duration']),utils.pretty_dict(utils.get_times(task)), None).show()
|
|
540
555
|
|
|
541
556
|
# Start randomness timer
|
|
542
|
-
self.session =
|
|
543
|
-
"label": 'Randomness',
|
|
544
|
-
"extended_label": 'Randomness',
|
|
545
|
-
"start_time": now(),
|
|
546
|
-
"duration":0,
|
|
547
|
-
"task":{}
|
|
548
|
-
}
|
|
557
|
+
self.session = default_session()
|
|
549
558
|
|
|
550
559
|
self.tick()
|
|
551
560
|
|
|
@@ -554,9 +563,6 @@ class Application(Gtk.Application):
|
|
|
554
563
|
self.menu.get_children()[0].destroy()
|
|
555
564
|
self.do_not_disturb_menu_item.set_label("Do Not Disturb")
|
|
556
565
|
|
|
557
|
-
else:
|
|
558
|
-
print('no task running!')
|
|
559
|
-
|
|
560
566
|
|
|
561
567
|
def task_running_menu_additions(self):
|
|
562
568
|
|
|
@@ -577,7 +583,7 @@ class Application(Gtk.Application):
|
|
|
577
583
|
|
|
578
584
|
def async_refresh(self, w=None, single_todo = None):
|
|
579
585
|
|
|
580
|
-
self.indicator.set_label("Refreshing Todolists", "
|
|
586
|
+
self.indicator.set_label("Refreshing Todolists", "Refreshing Todolists")
|
|
581
587
|
menu_item = Gtk.MenuItem.new_with_label("Refreshing Todolists")
|
|
582
588
|
self.menu.append(menu_item)
|
|
583
589
|
self.menu.show_all()
|
|
@@ -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'])
|