taskcheck 0.1.2__tar.gz → 0.1.3__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.1.2/taskcheck.egg-info → taskcheck-0.1.3}/PKG-INFO +6 -3
- {taskcheck-0.1.2 → taskcheck-0.1.3}/README.md +5 -2
- {taskcheck-0.1.2 → taskcheck-0.1.3}/pyproject.toml +1 -1
- {taskcheck-0.1.2 → taskcheck-0.1.3}/taskcheck/__main__.py +59 -28
- {taskcheck-0.1.2 → taskcheck-0.1.3/taskcheck.egg-info}/PKG-INFO +6 -3
- {taskcheck-0.1.2 → taskcheck-0.1.3}/LICENSE +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/setup.cfg +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/taskcheck/ical.py +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/taskcheck.egg-info/SOURCES.txt +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/taskcheck.egg-info/dependency_links.txt +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/taskcheck.egg-info/entry_points.txt +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/taskcheck.egg-info/requires.txt +0 -0
- {taskcheck-0.1.2 → taskcheck-0.1.3}/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.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Automatic scheduler for taskwarrior
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -45,6 +45,8 @@ You can exclude a task from being scheduled by removing the `time_map` or `estim
|
|
|
45
45
|
|
|
46
46
|
You can see tasks that you can execute now with the `task ready` report.
|
|
47
47
|
|
|
48
|
+
You can see the schedule for a task in the `scheduling` UDA.
|
|
49
|
+
|
|
48
50
|
## Configuration
|
|
49
51
|
|
|
50
52
|
1. Create a TOML file at `~/.config/task/taskcheck.toml` with the following format:
|
|
@@ -90,6 +92,8 @@ uda.estimated.type = duration
|
|
|
90
92
|
uda.estimated.label = Estimated Time
|
|
91
93
|
uda.completion_date.type = date
|
|
92
94
|
uda.completion_date.label = Expected Completion Date
|
|
95
|
+
uda.scheduling.type = string
|
|
96
|
+
uda.scheduling.label = Scheduling
|
|
93
97
|
|
|
94
98
|
# Adjust urgency for taskcheck
|
|
95
99
|
# you will use `wait:` to avoid scheduling tasks too much sooner
|
|
@@ -106,7 +110,7 @@ urgency.inherit=1 # use dependencies to automatically prioritize tasks
|
|
|
106
110
|
urgency.blocking.coefficient=2
|
|
107
111
|
|
|
108
112
|
# give urgeny values to the estimated time
|
|
109
|
-
# this is
|
|
113
|
+
# this is log(estimation) + 1, with base so that 36 hours has ~12 urgency
|
|
110
114
|
urgency.uda.estimated.PT1H.coefficient = 1
|
|
111
115
|
urgency.uda.estimated.PT2H.coefficient = 2.32
|
|
112
116
|
urgency.uda.estimated.PT3H.coefficient = 3.67
|
|
@@ -152,6 +156,5 @@ urgency.uda.estimated.PT36H.coefficient = 11.96
|
|
|
152
156
|
```
|
|
153
157
|
|
|
154
158
|
-v, --verbose: increase output verbosity
|
|
155
|
-
-t, --today: how much time has been already used today (default: 0)
|
|
156
159
|
|
|
157
160
|
```
|
|
@@ -34,6 +34,8 @@ You can exclude a task from being scheduled by removing the `time_map` or `estim
|
|
|
34
34
|
|
|
35
35
|
You can see tasks that you can execute now with the `task ready` report.
|
|
36
36
|
|
|
37
|
+
You can see the schedule for a task in the `scheduling` UDA.
|
|
38
|
+
|
|
37
39
|
## Configuration
|
|
38
40
|
|
|
39
41
|
1. Create a TOML file at `~/.config/task/taskcheck.toml` with the following format:
|
|
@@ -79,6 +81,8 @@ uda.estimated.type = duration
|
|
|
79
81
|
uda.estimated.label = Estimated Time
|
|
80
82
|
uda.completion_date.type = date
|
|
81
83
|
uda.completion_date.label = Expected Completion Date
|
|
84
|
+
uda.scheduling.type = string
|
|
85
|
+
uda.scheduling.label = Scheduling
|
|
82
86
|
|
|
83
87
|
# Adjust urgency for taskcheck
|
|
84
88
|
# you will use `wait:` to avoid scheduling tasks too much sooner
|
|
@@ -95,7 +99,7 @@ urgency.inherit=1 # use dependencies to automatically prioritize tasks
|
|
|
95
99
|
urgency.blocking.coefficient=2
|
|
96
100
|
|
|
97
101
|
# give urgeny values to the estimated time
|
|
98
|
-
# this is
|
|
102
|
+
# this is log(estimation) + 1, with base so that 36 hours has ~12 urgency
|
|
99
103
|
urgency.uda.estimated.PT1H.coefficient = 1
|
|
100
104
|
urgency.uda.estimated.PT2H.coefficient = 2.32
|
|
101
105
|
urgency.uda.estimated.PT3H.coefficient = 3.67
|
|
@@ -141,6 +145,5 @@ urgency.uda.estimated.PT36H.coefficient = 11.96
|
|
|
141
145
|
```
|
|
142
146
|
|
|
143
147
|
-v, --verbose: increase output verbosity
|
|
144
|
-
-t, --today: how much time has been already used today (default: 0)
|
|
145
148
|
|
|
146
149
|
```
|
|
@@ -11,14 +11,6 @@ from taskcheck.ical import ical_to_dict
|
|
|
11
11
|
config_dir = Path(appdirs.user_config_dir("task"))
|
|
12
12
|
|
|
13
13
|
arg_parser = argparse.ArgumentParser()
|
|
14
|
-
arg_parser.add_argument(
|
|
15
|
-
"-t",
|
|
16
|
-
"--today",
|
|
17
|
-
type=float,
|
|
18
|
-
action="store",
|
|
19
|
-
help="specify how many hours you have already worked today (default: 0)",
|
|
20
|
-
default=0,
|
|
21
|
-
)
|
|
22
14
|
arg_parser.add_argument(
|
|
23
15
|
"-v", "--verbose", action="store_true", help="increase output verbosity"
|
|
24
16
|
)
|
|
@@ -59,6 +51,7 @@ def _hours_to_time(hour):
|
|
|
59
51
|
|
|
60
52
|
|
|
61
53
|
def _time_to_decimal(time):
|
|
54
|
+
# round to 2 digits after the point
|
|
62
55
|
return time.hour + time.minute / 60
|
|
63
56
|
|
|
64
57
|
|
|
@@ -142,7 +135,20 @@ def get_long_range_time_map(time_maps, time_map_names, days_ahead, calendars):
|
|
|
142
135
|
task_time_map.append(daily_hours)
|
|
143
136
|
long_range_time_map[key] = task_time_map
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
today_time = _time_to_decimal(datetime.now().time())
|
|
139
|
+
today_weekday = datetime.today().strftime("%A").lower()
|
|
140
|
+
today_used_hours = 0
|
|
141
|
+
# compare with the time_maps of today
|
|
142
|
+
for time_map_name in time_map_names:
|
|
143
|
+
time_map = time_maps[time_map_name].get(today_weekday)
|
|
144
|
+
if time_map:
|
|
145
|
+
for schedule_start, schedule_end in time_map:
|
|
146
|
+
if schedule_start <= today_time <= schedule_end:
|
|
147
|
+
today_used_hours += today_time - schedule_start
|
|
148
|
+
break
|
|
149
|
+
elif today_time > schedule_end:
|
|
150
|
+
today_used_hours += schedule_end - schedule_start
|
|
151
|
+
return task_time_map, today_used_hours
|
|
146
152
|
|
|
147
153
|
|
|
148
154
|
def schedule_task_on_day(
|
|
@@ -155,14 +161,17 @@ def schedule_task_on_day(
|
|
|
155
161
|
today,
|
|
156
162
|
used_hours,
|
|
157
163
|
wait,
|
|
164
|
+
scheduling_note,
|
|
158
165
|
):
|
|
159
166
|
# we can schedule task on this day
|
|
160
167
|
employable_hours = task_time_map[day_offset] - used_hours[day_offset]
|
|
168
|
+
# avoid using too small values
|
|
169
|
+
employable_hours = 0.01 if 0.01 > employable_hours > 0 else employable_hours
|
|
161
170
|
current_date = today + timedelta(days=day_offset)
|
|
162
171
|
if wait and current_date <= wait:
|
|
163
172
|
if args.verbose:
|
|
164
173
|
print(f"Skipping date {current_date} because of wait date {wait}")
|
|
165
|
-
return start_date, end_date, task_remaining_hours, is_starting
|
|
174
|
+
return start_date, end_date, task_remaining_hours, is_starting, scheduling_note
|
|
166
175
|
|
|
167
176
|
if is_starting:
|
|
168
177
|
if args.verbose:
|
|
@@ -170,8 +179,12 @@ def schedule_task_on_day(
|
|
|
170
179
|
is_starting = False
|
|
171
180
|
start_date = current_date
|
|
172
181
|
|
|
173
|
-
|
|
182
|
+
# minimum value we admit is 0.01 (1 minute)
|
|
183
|
+
if task_remaining_hours <= employable_hours + 0.01:
|
|
174
184
|
# consume all the remaining task's hours
|
|
185
|
+
if scheduling_note != "":
|
|
186
|
+
scheduling_note += "\n"
|
|
187
|
+
scheduling_note += f"{current_date}: {task_remaining_hours:.2f} hours"
|
|
175
188
|
used_hours[day_offset] += task_remaining_hours
|
|
176
189
|
task_remaining_hours = 0
|
|
177
190
|
end_date = current_date
|
|
@@ -180,14 +193,19 @@ def schedule_task_on_day(
|
|
|
180
193
|
print(f"Used hours on {current_date}: {used_hours[day_offset]}")
|
|
181
194
|
else:
|
|
182
195
|
# consume all the available hours on this task
|
|
196
|
+
if scheduling_note != "":
|
|
197
|
+
scheduling_note += "\n"
|
|
198
|
+
scheduling_note += f"{current_date}: {employable_hours:.2f} hours"
|
|
183
199
|
task_remaining_hours -= employable_hours
|
|
184
200
|
used_hours[day_offset] += employable_hours
|
|
185
201
|
if args.verbose:
|
|
186
202
|
print(f"Working for {employable_hours} hours on task on {current_date}")
|
|
187
|
-
return start_date, end_date, task_remaining_hours, is_starting
|
|
203
|
+
return start_date, end_date, task_remaining_hours, is_starting, scheduling_note
|
|
188
204
|
|
|
189
205
|
|
|
190
|
-
def mark_end_date(
|
|
206
|
+
def mark_end_date(
|
|
207
|
+
due_date, end_date, start_date, scheduling_note, id, description=None
|
|
208
|
+
):
|
|
191
209
|
start_end_fields = [f"scheduled:{start_date}", f"completion_date:{end_date}"]
|
|
192
210
|
|
|
193
211
|
subprocess.run(
|
|
@@ -196,6 +214,7 @@ def mark_end_date(due_date, end_date, start_date, id, description=None):
|
|
|
196
214
|
str(id),
|
|
197
215
|
"modify",
|
|
198
216
|
*start_end_fields,
|
|
217
|
+
f'scheduling:"{scheduling_note}"',
|
|
199
218
|
],
|
|
200
219
|
stdout=subprocess.DEVNULL,
|
|
201
220
|
stderr=subprocess.DEVNULL,
|
|
@@ -230,7 +249,7 @@ def check_tasks_sequentially(tasks, config):
|
|
|
230
249
|
time_maps = config["time_maps"]
|
|
231
250
|
today = datetime.today().date()
|
|
232
251
|
todo = [True if t["status"] not in AVOID_STATUS else False for t in tasks]
|
|
233
|
-
used_hours = [
|
|
252
|
+
used_hours = [0] * (config["scheduler"]["days_ahead"] + 1)
|
|
234
253
|
calendars = get_calendars(config)
|
|
235
254
|
|
|
236
255
|
while any(todo):
|
|
@@ -268,33 +287,45 @@ def check_tasks_sequentially(tasks, config):
|
|
|
268
287
|
)
|
|
269
288
|
|
|
270
289
|
task_remaining_hours = estimated_hours
|
|
271
|
-
task_time_map = get_long_range_time_map(
|
|
290
|
+
task_time_map, today_used_hours = get_long_range_time_map(
|
|
272
291
|
time_maps, time_map_names, config["scheduler"]["days_ahead"], calendars
|
|
273
292
|
)
|
|
293
|
+
used_hours[0] += today_used_hours
|
|
274
294
|
|
|
275
295
|
# Simulate work day-by-day until task is complete or past due
|
|
276
296
|
is_starting = True
|
|
297
|
+
scheduling_note = ""
|
|
277
298
|
start_date = end_date = None
|
|
278
299
|
for offset in range(len(task_time_map)):
|
|
279
300
|
if task_time_map[offset] > used_hours[offset]:
|
|
280
|
-
(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
301
|
+
(
|
|
302
|
+
start_date,
|
|
303
|
+
end_date,
|
|
304
|
+
task_remaining_hours,
|
|
305
|
+
is_starting,
|
|
306
|
+
scheduling_note,
|
|
307
|
+
) = schedule_task_on_day(
|
|
308
|
+
is_starting,
|
|
309
|
+
offset,
|
|
310
|
+
start_date,
|
|
311
|
+
end_date,
|
|
312
|
+
task_remaining_hours,
|
|
313
|
+
task_time_map,
|
|
314
|
+
today,
|
|
315
|
+
used_hours,
|
|
316
|
+
wait_date,
|
|
317
|
+
scheduling_note,
|
|
292
318
|
)
|
|
293
319
|
|
|
294
320
|
if end_date is not None:
|
|
295
321
|
todo[i] = False
|
|
296
322
|
mark_end_date(
|
|
297
|
-
due_date,
|
|
323
|
+
due_date,
|
|
324
|
+
end_date,
|
|
325
|
+
start_date,
|
|
326
|
+
scheduling_note,
|
|
327
|
+
task["id"],
|
|
328
|
+
task["description"],
|
|
298
329
|
)
|
|
299
330
|
break
|
|
300
331
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: taskcheck
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Automatic scheduler for taskwarrior
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -45,6 +45,8 @@ You can exclude a task from being scheduled by removing the `time_map` or `estim
|
|
|
45
45
|
|
|
46
46
|
You can see tasks that you can execute now with the `task ready` report.
|
|
47
47
|
|
|
48
|
+
You can see the schedule for a task in the `scheduling` UDA.
|
|
49
|
+
|
|
48
50
|
## Configuration
|
|
49
51
|
|
|
50
52
|
1. Create a TOML file at `~/.config/task/taskcheck.toml` with the following format:
|
|
@@ -90,6 +92,8 @@ uda.estimated.type = duration
|
|
|
90
92
|
uda.estimated.label = Estimated Time
|
|
91
93
|
uda.completion_date.type = date
|
|
92
94
|
uda.completion_date.label = Expected Completion Date
|
|
95
|
+
uda.scheduling.type = string
|
|
96
|
+
uda.scheduling.label = Scheduling
|
|
93
97
|
|
|
94
98
|
# Adjust urgency for taskcheck
|
|
95
99
|
# you will use `wait:` to avoid scheduling tasks too much sooner
|
|
@@ -106,7 +110,7 @@ urgency.inherit=1 # use dependencies to automatically prioritize tasks
|
|
|
106
110
|
urgency.blocking.coefficient=2
|
|
107
111
|
|
|
108
112
|
# give urgeny values to the estimated time
|
|
109
|
-
# this is
|
|
113
|
+
# this is log(estimation) + 1, with base so that 36 hours has ~12 urgency
|
|
110
114
|
urgency.uda.estimated.PT1H.coefficient = 1
|
|
111
115
|
urgency.uda.estimated.PT2H.coefficient = 2.32
|
|
112
116
|
urgency.uda.estimated.PT3H.coefficient = 3.67
|
|
@@ -152,6 +156,5 @@ urgency.uda.estimated.PT36H.coefficient = 11.96
|
|
|
152
156
|
```
|
|
153
157
|
|
|
154
158
|
-v, --verbose: increase output verbosity
|
|
155
|
-
-t, --today: how much time has been already used today (default: 0)
|
|
156
159
|
|
|
157
160
|
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|