taskcheck 0.2.0__tar.gz → 0.2.1__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.
- {taskcheck-0.2.0/taskcheck.egg-info → taskcheck-0.2.1}/PKG-INFO +25 -13
- {taskcheck-0.2.0 → taskcheck-0.2.1}/README.md +24 -12
- {taskcheck-0.2.0 → taskcheck-0.2.1}/pyproject.toml +1 -1
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck/install.py +17 -6
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck/parallel.py +50 -25
- {taskcheck-0.2.0 → taskcheck-0.2.1/taskcheck.egg-info}/PKG-INFO +25 -13
- {taskcheck-0.2.0 → taskcheck-0.2.1}/LICENSE +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/setup.cfg +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck/__main__.py +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck/common.py +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck/ical.py +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck/sequential.py +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck.egg-info/SOURCES.txt +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck.egg-info/dependency_links.txt +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck.egg-info/entry_points.txt +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck.egg-info/requires.txt +0 -0
- {taskcheck-0.2.0 → taskcheck-0.2.1}/taskcheck.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: taskcheck
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Automatic scheduler for taskwarrior
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -19,7 +19,8 @@ This is a taskwarrior extension checks if tasks can be completed on time, consid
|
|
|
19
19
|
- [x] Use arbitrarily complex time maps
|
|
20
20
|
- [x] Use ical to block time from scheduling (e.g. for meetings, vacations, etc.)
|
|
21
21
|
- [x] Implement scheduling algorithm for parallely working on multiple tasks
|
|
22
|
-
- [ ] Use Google
|
|
22
|
+
- [ ] Use Google API to access calendars
|
|
23
|
+
- [ ] Export tasks to iCal calendar and API calendars
|
|
23
24
|
|
|
24
25
|
## Install
|
|
25
26
|
|
|
@@ -31,29 +32,33 @@ This is a taskwarrior extension checks if tasks can be completed on time, consid
|
|
|
31
32
|
This extension parses your pending and waiting tasks sorted decreasingly by urgency and tries to schedule them in the future.
|
|
32
33
|
It considers their estimated time to schedule all tasks starting from the most urgent one.
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Taskcheck leverages to UDAs, `estimated` and `time_map`. The `estimated` attribute is
|
|
35
36
|
the expected time to complete the task in hours. The `time_map` is a comma-separated list of strings
|
|
36
37
|
that indicates the hours per day in which you will work on a task (e.g. `work`, `weekend`, etc.).
|
|
37
38
|
The exact correspondence between the `time_map` and the hours of the day is defined in the configuration
|
|
38
|
-
file of taskcheck.
|
|
39
|
+
file of taskcheck. For instance:
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
```toml
|
|
42
|
+
```toml
|
|
43
|
+
[time_maps]
|
|
44
|
+
# get an error)
|
|
45
|
+
[time_maps.work]
|
|
46
|
+
monday = [[9, 12.30], [14, 17]]
|
|
47
|
+
tuesday = [[9, 12.30], [14, 17]]
|
|
48
|
+
# ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Taskcheck will also parse online iCal calendars (Google, Apple, etc.) and will match them with your time maps.
|
|
52
|
+
It will then modify the Taskwarrior tasks by adding the `completion_date` attribute with the expected
|
|
41
53
|
date of completion and the `scheduled` attribute with the date in which the task is expected to
|
|
42
54
|
start.
|
|
43
55
|
|
|
44
56
|
It will also print a red line for every task whose `completion_date` is after its `due_date`.
|
|
45
57
|
|
|
46
|
-
You can exclude a task from being scheduled by removing the `time_map` or `estimated` attributes.
|
|
47
|
-
|
|
48
|
-
You can see tasks that you can execute now with the `task ready` report.
|
|
49
|
-
|
|
50
|
-
You can see the schedule for a task in the `scheduling` UDA, e.g. `task scheduling:$(task calc
|
|
51
|
-
tomorrow)` will show you the tasks that include a scheduling for tomorrow.
|
|
52
|
-
|
|
53
58
|
In general, it is recommended to run taskcheck rather frequently and at least once at the beginning
|
|
54
59
|
of your working day.
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
## Algorithms
|
|
57
62
|
|
|
58
63
|
Taskcheck provides two scheduling algorithms: `parallel` and `sequential`.
|
|
59
64
|
|
|
@@ -125,6 +130,13 @@ event_all_day_is_blocking = true
|
|
|
125
130
|
expiration = 720 # in hours (720 hours = 30 days)
|
|
126
131
|
```
|
|
127
132
|
|
|
133
|
+
## Tips and Tricks
|
|
134
|
+
|
|
135
|
+
- You can exclude a task from being scheduled by removing the `time_map` or `estimated` attributes.
|
|
136
|
+
- You can see tasks that you can execute now with the `task ready` report.
|
|
137
|
+
- You can see the schedule for a task in the `scheduling` UDA, e.g. `task scheduling:$(task calc
|
|
138
|
+
tomorrow)` will show you the tasks that include a scheduling for tomorrow.
|
|
139
|
+
|
|
128
140
|
## CLI Options
|
|
129
141
|
|
|
130
142
|
```
|
|
@@ -8,7 +8,8 @@ This is a taskwarrior extension checks if tasks can be completed on time, consid
|
|
|
8
8
|
- [x] Use arbitrarily complex time maps
|
|
9
9
|
- [x] Use ical to block time from scheduling (e.g. for meetings, vacations, etc.)
|
|
10
10
|
- [x] Implement scheduling algorithm for parallely working on multiple tasks
|
|
11
|
-
- [ ] Use Google
|
|
11
|
+
- [ ] Use Google API to access calendars
|
|
12
|
+
- [ ] Export tasks to iCal calendar and API calendars
|
|
12
13
|
|
|
13
14
|
## Install
|
|
14
15
|
|
|
@@ -20,29 +21,33 @@ This is a taskwarrior extension checks if tasks can be completed on time, consid
|
|
|
20
21
|
This extension parses your pending and waiting tasks sorted decreasingly by urgency and tries to schedule them in the future.
|
|
21
22
|
It considers their estimated time to schedule all tasks starting from the most urgent one.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
Taskcheck leverages to UDAs, `estimated` and `time_map`. The `estimated` attribute is
|
|
24
25
|
the expected time to complete the task in hours. The `time_map` is a comma-separated list of strings
|
|
25
26
|
that indicates the hours per day in which you will work on a task (e.g. `work`, `weekend`, etc.).
|
|
26
27
|
The exact correspondence between the `time_map` and the hours of the day is defined in the configuration
|
|
27
|
-
file of taskcheck.
|
|
28
|
+
file of taskcheck. For instance:
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
```toml
|
|
31
|
+
```toml
|
|
32
|
+
[time_maps]
|
|
33
|
+
# get an error)
|
|
34
|
+
[time_maps.work]
|
|
35
|
+
monday = [[9, 12.30], [14, 17]]
|
|
36
|
+
tuesday = [[9, 12.30], [14, 17]]
|
|
37
|
+
# ...
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Taskcheck will also parse online iCal calendars (Google, Apple, etc.) and will match them with your time maps.
|
|
41
|
+
It will then modify the Taskwarrior tasks by adding the `completion_date` attribute with the expected
|
|
30
42
|
date of completion and the `scheduled` attribute with the date in which the task is expected to
|
|
31
43
|
start.
|
|
32
44
|
|
|
33
45
|
It will also print a red line for every task whose `completion_date` is after its `due_date`.
|
|
34
46
|
|
|
35
|
-
You can exclude a task from being scheduled by removing the `time_map` or `estimated` attributes.
|
|
36
|
-
|
|
37
|
-
You can see tasks that you can execute now with the `task ready` report.
|
|
38
|
-
|
|
39
|
-
You can see the schedule for a task in the `scheduling` UDA, e.g. `task scheduling:$(task calc
|
|
40
|
-
tomorrow)` will show you the tasks that include a scheduling for tomorrow.
|
|
41
|
-
|
|
42
47
|
In general, it is recommended to run taskcheck rather frequently and at least once at the beginning
|
|
43
48
|
of your working day.
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
## Algorithms
|
|
46
51
|
|
|
47
52
|
Taskcheck provides two scheduling algorithms: `parallel` and `sequential`.
|
|
48
53
|
|
|
@@ -114,6 +119,13 @@ event_all_day_is_blocking = true
|
|
|
114
119
|
expiration = 720 # in hours (720 hours = 30 days)
|
|
115
120
|
```
|
|
116
121
|
|
|
122
|
+
## Tips and Tricks
|
|
123
|
+
|
|
124
|
+
- You can exclude a task from being scheduled by removing the `time_map` or `estimated` attributes.
|
|
125
|
+
- You can see tasks that you can execute now with the `task ready` report.
|
|
126
|
+
- You can see the schedule for a task in the `scheduling` UDA, e.g. `task scheduling:$(task calc
|
|
127
|
+
tomorrow)` will show you the tasks that include a scheduling for tomorrow.
|
|
128
|
+
|
|
117
129
|
## CLI Options
|
|
118
130
|
|
|
119
131
|
```
|
|
@@ -88,26 +88,35 @@ def install():
|
|
|
88
88
|
),
|
|
89
89
|
("journal.info", "0"),
|
|
90
90
|
("urgency.inherit", "1"),
|
|
91
|
+
("urgency.blocked.coefficient", "0"),
|
|
92
|
+
("urgency.blocking.coefficient", "0"),
|
|
93
|
+
("urgency.waiting.coefficient", "0"),
|
|
94
|
+
("urgency.scheduled.coefficient", "0"),
|
|
91
95
|
]
|
|
92
96
|
|
|
93
97
|
answer = input(
|
|
94
|
-
"""
|
|
98
|
+
"""
|
|
99
|
+
_________________________________________________________________________________________
|
|
100
|
+
Required configuration for taskcheck includes:
|
|
95
101
|
- 3 UDA fields
|
|
96
102
|
- turning off confirmation for recurring tasks
|
|
97
103
|
|
|
98
|
-
Do you want to continue? [y/N]"""
|
|
104
|
+
Do you want to continue? [y/N] """
|
|
99
105
|
)
|
|
100
106
|
if answer.lower() not in ["y", "yes"]:
|
|
101
107
|
return
|
|
102
108
|
|
|
103
109
|
answer = input(
|
|
104
|
-
"""
|
|
110
|
+
"""
|
|
111
|
+
_________________________________________________________________________________________
|
|
112
|
+
Optional configuration for taskcheck includes:
|
|
105
113
|
- urgency coefficients for estimated time (up to 36 hour duration)
|
|
106
114
|
- better `ready` report columns
|
|
107
115
|
- turning off printing journal logs in the task details
|
|
108
|
-
- turning on urgency inheritance from dependant tasks
|
|
116
|
+
- turning on urgency inheritance from dependant tasks and disabling blocked and blocking urgency coefficients (as recommended by Taskwarrior documentation)
|
|
117
|
+
- turning off urgency for waiting and scheduled tasks (taskcheck already take them into account)
|
|
109
118
|
|
|
110
|
-
Do you want to apply optional configurations? [y/N]"""
|
|
119
|
+
Do you want to apply optional configurations? [y/N] """
|
|
111
120
|
)
|
|
112
121
|
if answer.lower() not in ["y", "yes"]:
|
|
113
122
|
configs = required_configs
|
|
@@ -123,7 +132,9 @@ Do you want to apply optional configurations? [y/N]"""
|
|
|
123
132
|
print(f"Failed to apply configurations: {e}")
|
|
124
133
|
|
|
125
134
|
answer = input(
|
|
126
|
-
"""
|
|
135
|
+
"""
|
|
136
|
+
_________________________________________________________________________________________
|
|
137
|
+
Do you want to create a default config file for taskcheck? [y/N] """
|
|
127
138
|
)
|
|
128
139
|
if answer.lower() not in ["y", "yes"]:
|
|
129
140
|
return
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import subprocess
|
|
2
2
|
import re
|
|
3
|
+
from dataclasses import dataclass
|
|
3
4
|
|
|
4
5
|
from datetime import datetime, timedelta
|
|
5
6
|
from taskcheck.common import (
|
|
@@ -13,6 +14,13 @@ from taskcheck.common import (
|
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
@dataclass
|
|
18
|
+
class UrgencyCoefficients:
|
|
19
|
+
estimated: dict
|
|
20
|
+
inherit: bool
|
|
21
|
+
active: float
|
|
22
|
+
|
|
23
|
+
|
|
16
24
|
def get_urgency_coefficients():
|
|
17
25
|
"""
|
|
18
26
|
Retrieves urgency coefficients from Taskwarrior configurations.
|
|
@@ -21,23 +29,31 @@ def get_urgency_coefficients():
|
|
|
21
29
|
"""
|
|
22
30
|
result = subprocess.run(["task", "_show"], capture_output=True, text=True)
|
|
23
31
|
inherit_urgency = False
|
|
32
|
+
active_task_coefficient = 0
|
|
24
33
|
coefficients = {}
|
|
25
34
|
pattern1 = re.compile(r"^urgency\.uda\.estimated\.(.+)\.coefficient=(.+)$")
|
|
26
35
|
pattern2 = re.compile(r"^urgency\.inherit=(.+)$")
|
|
36
|
+
pattern3 = re.compile(r"^urgency\.active.coefficient=(.+)$")
|
|
27
37
|
for line in result.stdout.splitlines():
|
|
28
38
|
match = pattern1.match(line)
|
|
29
39
|
if match:
|
|
30
40
|
estimated_value = match.group(1)
|
|
31
41
|
coefficient = float(match.group(2))
|
|
32
42
|
coefficients[estimated_value] = coefficient
|
|
43
|
+
|
|
33
44
|
match = pattern2.match(line)
|
|
34
45
|
if match:
|
|
35
46
|
inherit_urgency = match.group(1) == "1"
|
|
36
47
|
|
|
37
|
-
|
|
48
|
+
match = pattern3.match(line)
|
|
49
|
+
if match:
|
|
50
|
+
active_coefficient = float(match.group(1))
|
|
51
|
+
active_task_coefficient = active_coefficient
|
|
52
|
+
|
|
53
|
+
return UrgencyCoefficients(coefficients, inherit_urgency, active_task_coefficient)
|
|
38
54
|
|
|
39
55
|
|
|
40
|
-
def compute_estimated_urgency(remaining_hours,
|
|
56
|
+
def compute_estimated_urgency(remaining_hours, urgency_coefficients):
|
|
41
57
|
"""
|
|
42
58
|
Computes the estimated urgency for the given remaining hours using the coefficients.
|
|
43
59
|
"""
|
|
@@ -45,10 +61,10 @@ def compute_estimated_urgency(remaining_hours, coefficients):
|
|
|
45
61
|
pdth_value = hours_to_pdth(remaining_hours)
|
|
46
62
|
# Find the closest match (e.g., if '2h' is not available, use '1h' or '3h')
|
|
47
63
|
closest_match = min(
|
|
48
|
-
|
|
64
|
+
urgency_coefficients.estimated.keys(),
|
|
49
65
|
key=lambda x: abs(int(pdth_value[1]) - int(pdth_to_hours(x[1]))),
|
|
50
66
|
)
|
|
51
|
-
coefficient =
|
|
67
|
+
coefficient = urgency_coefficients.estimated[closest_match]
|
|
52
68
|
# Compute the urgency
|
|
53
69
|
estimated_urgency = coefficient * remaining_hours
|
|
54
70
|
return estimated_urgency
|
|
@@ -60,7 +76,7 @@ def check_tasks_parallel(config, verbose=False):
|
|
|
60
76
|
days_ahead = config["scheduler"]["days_ahead"]
|
|
61
77
|
calendars = get_calendars(config)
|
|
62
78
|
today = datetime.today().date()
|
|
63
|
-
urgency_coefficients
|
|
79
|
+
urgency_coefficients = get_urgency_coefficients()
|
|
64
80
|
|
|
65
81
|
task_info = initialize_task_info(
|
|
66
82
|
tasks, time_maps, days_ahead, urgency_coefficients, calendars
|
|
@@ -69,7 +85,7 @@ def check_tasks_parallel(config, verbose=False):
|
|
|
69
85
|
for day_offset in range(days_ahead):
|
|
70
86
|
date = today + timedelta(days=day_offset)
|
|
71
87
|
allocate_time_for_day(
|
|
72
|
-
task_info, day_offset, date, urgency_coefficients,
|
|
88
|
+
task_info, day_offset, date, urgency_coefficients, verbose
|
|
73
89
|
)
|
|
74
90
|
|
|
75
91
|
update_tasks_with_scheduling_info(task_info, verbose)
|
|
@@ -100,13 +116,12 @@ def initialize_task_info(tasks, time_maps, days_ahead, urgency_coefficients, cal
|
|
|
100
116
|
"scheduling": {},
|
|
101
117
|
"urgency": initial_urgency,
|
|
102
118
|
"estimated_urgency": estimated_urgency,
|
|
119
|
+
"started": False,
|
|
103
120
|
}
|
|
104
121
|
return task_info
|
|
105
122
|
|
|
106
123
|
|
|
107
|
-
def allocate_time_for_day(
|
|
108
|
-
task_info, day_offset, date, urgency_coefficients, inherit_urgency, verbose
|
|
109
|
-
):
|
|
124
|
+
def allocate_time_for_day(task_info, day_offset, date, urgency_coefficients, verbose):
|
|
110
125
|
total_available_hours = compute_total_available_hours(task_info, day_offset)
|
|
111
126
|
if verbose:
|
|
112
127
|
print(f"Day {date}, total available hours: {total_available_hours:.2f}")
|
|
@@ -117,7 +132,7 @@ def allocate_time_for_day(
|
|
|
117
132
|
tasks_remaining = prepare_tasks_remaining(task_info, day_offset)
|
|
118
133
|
|
|
119
134
|
while day_remaining_hours > 0 and tasks_remaining:
|
|
120
|
-
recompute_urgencies(tasks_remaining, urgency_coefficients
|
|
135
|
+
recompute_urgencies(tasks_remaining, urgency_coefficients)
|
|
121
136
|
sorted_task_ids = sorted(
|
|
122
137
|
tasks_remaining.keys(),
|
|
123
138
|
key=lambda x: (tasks_remaining[x]["urgency"], x),
|
|
@@ -125,10 +140,14 @@ def allocate_time_for_day(
|
|
|
125
140
|
)
|
|
126
141
|
|
|
127
142
|
allocated = False
|
|
128
|
-
for
|
|
129
|
-
if
|
|
143
|
+
for uuid in sorted_task_ids:
|
|
144
|
+
if uuid not in tasks_remaining:
|
|
145
|
+
# already completed
|
|
146
|
+
continue
|
|
147
|
+
info = tasks_remaining[uuid]
|
|
148
|
+
if any(d in tasks_remaining for d in info["task"].get("depends", [])):
|
|
149
|
+
# cannot execute this task until all its dependencies are completed
|
|
130
150
|
continue
|
|
131
|
-
info = tasks_remaining[id]
|
|
132
151
|
allocation = allocate_time_to_task(info, day_offset, day_remaining_hours)
|
|
133
152
|
if allocation > 0:
|
|
134
153
|
day_remaining_hours -= allocation
|
|
@@ -143,7 +162,7 @@ def allocate_time_for_day(
|
|
|
143
162
|
info["remaining_hours"] <= 0
|
|
144
163
|
or info["task_time_map"][day_offset] <= 0
|
|
145
164
|
):
|
|
146
|
-
del tasks_remaining[
|
|
165
|
+
del tasks_remaining[uuid]
|
|
147
166
|
# if day_remaining_hours <= 0:
|
|
148
167
|
break
|
|
149
168
|
if not allocated:
|
|
@@ -168,13 +187,13 @@ def compute_total_available_hours(task_info, day_offset):
|
|
|
168
187
|
|
|
169
188
|
def prepare_tasks_remaining(task_info, day_offset):
|
|
170
189
|
return {
|
|
171
|
-
info["task"]["
|
|
190
|
+
info["task"]["uuid"]: info
|
|
172
191
|
for info in task_info.values()
|
|
173
192
|
if info["remaining_hours"] > 0 and info["task_time_map"][day_offset] > 0
|
|
174
193
|
}
|
|
175
194
|
|
|
176
195
|
|
|
177
|
-
def recompute_urgencies(tasks_remaining, urgency_coefficients
|
|
196
|
+
def recompute_urgencies(tasks_remaining, urgency_coefficients):
|
|
178
197
|
# Recompute estimated urgencies as before
|
|
179
198
|
for info in tasks_remaining.values():
|
|
180
199
|
remaining_hours = info["remaining_hours"]
|
|
@@ -184,23 +203,28 @@ def recompute_urgencies(tasks_remaining, urgency_coefficients, inherit_urgency):
|
|
|
184
203
|
_old_estimated_urgency = info["estimated_urgency"]
|
|
185
204
|
info["estimated_urgency"] = estimated_urgency
|
|
186
205
|
info["urgency"] = info["urgency"] - _old_estimated_urgency + estimated_urgency
|
|
206
|
+
started_by_user = info["task"].get("start", "") != ""
|
|
207
|
+
started_by_scheduler = info["started"]
|
|
208
|
+
if started_by_scheduler and not started_by_user:
|
|
209
|
+
# If the task was started by the scheduler, apply the active task coefficient
|
|
210
|
+
info["urgency"] += urgency_coefficients.active
|
|
187
211
|
|
|
188
|
-
if
|
|
212
|
+
if urgency_coefficients.inherit:
|
|
189
213
|
# Define a recursive function to compute the maximum urgency
|
|
190
214
|
def get_max_urgency(info, visited):
|
|
191
|
-
|
|
192
|
-
if
|
|
193
|
-
return visited[
|
|
215
|
+
task_uuid = info["task"]["uuid"]
|
|
216
|
+
if task_uuid in visited:
|
|
217
|
+
return visited[task_uuid] # Return cached value to avoid cycles
|
|
194
218
|
# Start with the current task's urgency
|
|
195
219
|
urgency = info["urgency"]
|
|
196
|
-
visited[
|
|
220
|
+
visited[task_uuid] = urgency # Mark as visited
|
|
197
221
|
# Recursively compute urgencies of dependents
|
|
198
|
-
for
|
|
199
|
-
if
|
|
200
|
-
dep_info = tasks_remaining[
|
|
222
|
+
for dep_uuid in info["task"].get("depends", []):
|
|
223
|
+
if dep_uuid in tasks_remaining:
|
|
224
|
+
dep_info = tasks_remaining[dep_uuid]
|
|
201
225
|
dep_urgency = get_max_urgency(dep_info, visited)
|
|
202
226
|
urgency = max(urgency, dep_urgency)
|
|
203
|
-
visited[
|
|
227
|
+
visited[task_uuid] = urgency # Update with the maximum urgency found
|
|
204
228
|
return urgency
|
|
205
229
|
|
|
206
230
|
# Update urgencies based on dependents
|
|
@@ -227,6 +251,7 @@ def allocate_time_to_task(info, day_offset, day_remaining_hours):
|
|
|
227
251
|
|
|
228
252
|
info["remaining_hours"] -= allocation
|
|
229
253
|
info["task_time_map"][day_offset] -= allocation
|
|
254
|
+
info["started"] = True
|
|
230
255
|
|
|
231
256
|
return allocation
|
|
232
257
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: taskcheck
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Automatic scheduler for taskwarrior
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -19,7 +19,8 @@ This is a taskwarrior extension checks if tasks can be completed on time, consid
|
|
|
19
19
|
- [x] Use arbitrarily complex time maps
|
|
20
20
|
- [x] Use ical to block time from scheduling (e.g. for meetings, vacations, etc.)
|
|
21
21
|
- [x] Implement scheduling algorithm for parallely working on multiple tasks
|
|
22
|
-
- [ ] Use Google
|
|
22
|
+
- [ ] Use Google API to access calendars
|
|
23
|
+
- [ ] Export tasks to iCal calendar and API calendars
|
|
23
24
|
|
|
24
25
|
## Install
|
|
25
26
|
|
|
@@ -31,29 +32,33 @@ This is a taskwarrior extension checks if tasks can be completed on time, consid
|
|
|
31
32
|
This extension parses your pending and waiting tasks sorted decreasingly by urgency and tries to schedule them in the future.
|
|
32
33
|
It considers their estimated time to schedule all tasks starting from the most urgent one.
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Taskcheck leverages to UDAs, `estimated` and `time_map`. The `estimated` attribute is
|
|
35
36
|
the expected time to complete the task in hours. The `time_map` is a comma-separated list of strings
|
|
36
37
|
that indicates the hours per day in which you will work on a task (e.g. `work`, `weekend`, etc.).
|
|
37
38
|
The exact correspondence between the `time_map` and the hours of the day is defined in the configuration
|
|
38
|
-
file of taskcheck.
|
|
39
|
+
file of taskcheck. For instance:
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
```toml
|
|
42
|
+
```toml
|
|
43
|
+
[time_maps]
|
|
44
|
+
# get an error)
|
|
45
|
+
[time_maps.work]
|
|
46
|
+
monday = [[9, 12.30], [14, 17]]
|
|
47
|
+
tuesday = [[9, 12.30], [14, 17]]
|
|
48
|
+
# ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Taskcheck will also parse online iCal calendars (Google, Apple, etc.) and will match them with your time maps.
|
|
52
|
+
It will then modify the Taskwarrior tasks by adding the `completion_date` attribute with the expected
|
|
41
53
|
date of completion and the `scheduled` attribute with the date in which the task is expected to
|
|
42
54
|
start.
|
|
43
55
|
|
|
44
56
|
It will also print a red line for every task whose `completion_date` is after its `due_date`.
|
|
45
57
|
|
|
46
|
-
You can exclude a task from being scheduled by removing the `time_map` or `estimated` attributes.
|
|
47
|
-
|
|
48
|
-
You can see tasks that you can execute now with the `task ready` report.
|
|
49
|
-
|
|
50
|
-
You can see the schedule for a task in the `scheduling` UDA, e.g. `task scheduling:$(task calc
|
|
51
|
-
tomorrow)` will show you the tasks that include a scheduling for tomorrow.
|
|
52
|
-
|
|
53
58
|
In general, it is recommended to run taskcheck rather frequently and at least once at the beginning
|
|
54
59
|
of your working day.
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
## Algorithms
|
|
57
62
|
|
|
58
63
|
Taskcheck provides two scheduling algorithms: `parallel` and `sequential`.
|
|
59
64
|
|
|
@@ -125,6 +130,13 @@ event_all_day_is_blocking = true
|
|
|
125
130
|
expiration = 720 # in hours (720 hours = 30 days)
|
|
126
131
|
```
|
|
127
132
|
|
|
133
|
+
## Tips and Tricks
|
|
134
|
+
|
|
135
|
+
- You can exclude a task from being scheduled by removing the `time_map` or `estimated` attributes.
|
|
136
|
+
- You can see tasks that you can execute now with the `task ready` report.
|
|
137
|
+
- You can see the schedule for a task in the `scheduling` UDA, e.g. `task scheduling:$(task calc
|
|
138
|
+
tomorrow)` will show you the tasks that include a scheduling for tomorrow.
|
|
139
|
+
|
|
128
140
|
## CLI Options
|
|
129
141
|
|
|
130
142
|
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|