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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: taskcheck
3
- Version: 0.1.2
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 ln(estimation)
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 ln(estimation)
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
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskcheck"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  description = "Automatic scheduler for taskwarrior"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -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
- return task_time_map
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
- if task_remaining_hours <= employable_hours:
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(due_date, end_date, start_date, id, description=None):
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 = [args.today] + [0] * config["scheduler"]["days_ahead"]
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
- (start_date, end_date, task_remaining_hours, is_starting) = (
281
- schedule_task_on_day(
282
- is_starting,
283
- offset,
284
- start_date,
285
- end_date,
286
- task_remaining_hours,
287
- task_time_map,
288
- today,
289
- used_hours,
290
- wait_date,
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, end_date, start_date, task["id"], task["description"]
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.2
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 ln(estimation)
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