taskcheck 0.3.2.post2__tar.gz → 0.3.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.3.2.post2
3
+ Version: 0.3.3
4
4
  Summary: Automatic scheduler for taskwarrior
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskcheck"
3
- version = "0.3.2-2"
3
+ version = "0.3.3"
4
4
  description = "Automatic scheduler for taskwarrior"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -20,6 +20,9 @@ class UrgencyCoefficients:
20
20
  estimated: dict
21
21
  inherit: bool
22
22
  active: float
23
+ age_max: int
24
+ urgency_due: int
25
+ urgency_age: int
23
26
 
24
27
 
25
28
  def get_urgency_coefficients():
@@ -31,16 +34,22 @@ def get_urgency_coefficients():
31
34
  result = subprocess.run(["task", "_show"], capture_output=True, text=True)
32
35
  inherit_urgency = False
33
36
  active_task_coefficient = 0
34
- coefficients = {}
37
+ est_coeffs = {}
38
+ urgency_age_max = 0
39
+ urgency_age = 0
40
+ urgency_due = 0
35
41
  pattern1 = re.compile(r"^urgency\.uda\.estimated\.(.+)\.coefficient=(.+)$")
36
42
  pattern2 = re.compile(r"^urgency\.inherit=(.+)$")
37
- pattern3 = re.compile(r"^urgency\.active.coefficient=(.+)$")
43
+ pattern3 = re.compile(r"^urgency\.active\.coefficient=(.+)$")
44
+ pattern4 = re.compile(r"^urgency\.age\.max=(.+)$")
45
+ pattern5 = re.compile(r"^urgency\.due\.coefficient=(.+)$")
46
+ pattern6 = re.compile(r"^urgency\.age\.coefficient=(.+)$")
38
47
  for line in result.stdout.splitlines():
39
48
  match = pattern1.match(line)
40
49
  if match:
41
50
  estimated_value = match.group(1)
42
51
  coefficient = float(match.group(2))
43
- coefficients[estimated_value] = coefficient
52
+ est_coeffs[estimated_value] = coefficient
44
53
 
45
54
  match = pattern2.match(line)
46
55
  if match:
@@ -51,20 +60,26 @@ def get_urgency_coefficients():
51
60
  active_coefficient = float(match.group(1))
52
61
  active_task_coefficient = active_coefficient
53
62
 
54
- return UrgencyCoefficients(coefficients, inherit_urgency, active_task_coefficient)
63
+ match = pattern4.match(line)
64
+ if match:
65
+ urgency_age_max = int(match.group(1))
55
66
 
67
+ match = pattern5.match(line)
68
+ if match:
69
+ urgency_due = int(match.group(1))
56
70
 
57
- def compute_estimated_urgency(remaining_hours, urgency_coefficients):
58
- """
59
- Computes the estimated urgency for the given remaining hours using the coefficients.
60
- """
61
- # Find the closest match (e.g., if '2h' is not available, use '1h' or '3h')
62
- closest_match = min(
63
- urgency_coefficients.estimated.keys(),
64
- key=lambda x: abs(pdth_to_hours(x) - remaining_hours),
71
+ match = pattern6.match(line)
72
+ if match:
73
+ urgency_age = int(match.group(1))
74
+
75
+ return UrgencyCoefficients(
76
+ est_coeffs,
77
+ inherit_urgency,
78
+ active_task_coefficient,
79
+ urgency_age_max,
80
+ urgency_due,
81
+ urgency_age,
65
82
  )
66
- coefficient = urgency_coefficients.estimated[closest_match]
67
- return coefficient
68
83
 
69
84
 
70
85
  def check_tasks_parallel(config, verbose=False):
@@ -72,7 +87,6 @@ def check_tasks_parallel(config, verbose=False):
72
87
  time_maps = config["time_maps"]
73
88
  days_ahead = config["scheduler"]["days_ahead"]
74
89
  calendars = get_calendars(config)
75
- today = datetime.today().date()
76
90
  urgency_coefficients = get_urgency_coefficients()
77
91
 
78
92
  task_info = initialize_task_info(
@@ -87,6 +101,7 @@ def check_tasks_parallel(config, verbose=False):
87
101
 
88
102
  def initialize_task_info(tasks, time_maps, days_ahead, urgency_coefficients, calendars):
89
103
  task_info = {}
104
+ today = datetime.today().date()
90
105
  for task in tasks:
91
106
  if task.get("status") in AVOID_STATUS:
92
107
  continue
@@ -99,9 +114,11 @@ def initialize_task_info(tasks, time_maps, days_ahead, urgency_coefficients, cal
99
114
  )
100
115
  task_uuid = task["uuid"]
101
116
  initial_urgency = float(task.get("urgency", 0))
102
- estimated_urgency = compute_estimated_urgency(
103
- estimated_hours, urgency_coefficients
117
+ estimated_urgency = urgency_estimated(
118
+ {"remaining_hours": estimated_hours}, None, urgency_coefficients
104
119
  )
120
+ due_urgency = urgency_due({"task": task}, today, urgency_coefficients)
121
+ age_urgency = urgency_age({"task": task}, today, urgency_coefficients)
105
122
  task_info[task_uuid] = {
106
123
  "task": task,
107
124
  "remaining_hours": estimated_hours,
@@ -110,6 +127,8 @@ def initialize_task_info(tasks, time_maps, days_ahead, urgency_coefficients, cal
110
127
  "scheduling": {},
111
128
  "urgency": initial_urgency,
112
129
  "estimated_urgency": estimated_urgency,
130
+ "due_urgency": due_urgency,
131
+ "age_urgency": age_urgency,
113
132
  "started": False,
114
133
  }
115
134
  return task_info
@@ -127,7 +146,7 @@ def allocate_time_for_day(task_info, day_offset, urgency_coefficients, verbose):
127
146
  tasks_remaining = prepare_tasks_remaining(task_info, day_offset)
128
147
 
129
148
  while day_remaining_hours > 0 and tasks_remaining:
130
- recompute_urgencies(tasks_remaining, urgency_coefficients)
149
+ recompute_urgencies(tasks_remaining, urgency_coefficients, date)
131
150
  sorted_task_ids = sorted(
132
151
  tasks_remaining.keys(),
133
152
  key=lambda x: tasks_remaining[x]["urgency"],
@@ -205,16 +224,72 @@ def prepare_tasks_remaining(task_info, day_offset):
205
224
  }
206
225
 
207
226
 
208
- def recompute_urgencies(tasks_remaining, urgency_coefficients):
227
+ def urgency_due(info, date, urgency_coefficients):
228
+ lfs = 0
229
+ task = info["task"]
230
+ if "due" in task:
231
+ due = datetime.strptime(task["due"], "%Y%m%dT%H%M%SZ").date()
232
+ # Map a range of 21 days to the value 0.2 - 1.0
233
+ days_overdue = (date - due).days
234
+ if days_overdue >= 7.0:
235
+ lfs = 1.0 # < 1 wk ago
236
+ elif days_overdue >= -14.0:
237
+ lfs = ((days_overdue + 14.0) * 0.8 / 21.0) + 0.2
238
+ else:
239
+ lfs = 0.2 # 2 wks
240
+ return lfs * urgency_coefficients.urgency_due
241
+
242
+
243
+ def urgency_age(info, date, urgency_coefficients):
244
+ urgency_age_max = urgency_coefficients.age_max
245
+ lfs = 1.0
246
+ task = info["task"]
247
+ if "entry" not in task:
248
+ return 1.0
249
+ entry = datetime.strptime(task["entry"], "%Y%m%dT%H%M%SZ").date()
250
+ age = (date - entry).days # in days
251
+ if urgency_age_max == 0 or age >= urgency_age_max:
252
+ lfs = 1.0
253
+ return lfs * age / urgency_age_max * urgency_coefficients.urgency_age
254
+
255
+
256
+ def urgency_estimated(info, date, urgency_coefficients):
257
+ """
258
+ Computes the estimated urgency for the given remaining hours using the coefficients.
259
+ """
260
+ # Find the closest match (e.g., if '2h' is not available, use '1h' or '3h')
261
+ closest_match = min(
262
+ urgency_coefficients.estimated.keys(),
263
+ key=lambda x: abs(pdth_to_hours(x) - info["remaining_hours"]),
264
+ )
265
+ coefficient = urgency_coefficients.estimated[closest_match]
266
+ return coefficient
267
+
268
+
269
+ def update_urgency(info, urgency_key, urgency_compute_fn, urgency_coefficients, date):
270
+ urgency_value = urgency_compute_fn(info, date, urgency_coefficients)
271
+ old_urgency = info[urgency_key]
272
+ if old_urgency != urgency_value:
273
+ print(
274
+ f"new urgency value for task {info['task']['id']}: {urgency_key}: {urgency_value}"
275
+ )
276
+ info[urgency_key] = urgency_value
277
+ info["urgency"] = info["urgency"] - old_urgency + urgency_value
278
+
279
+
280
+ def recompute_urgencies(tasks_remaining, urgency_coefficients, date):
281
+ """Recompute urgency simulating that today is `date`"""
209
282
  # Recompute estimated urgencies as before
210
283
  for info in tasks_remaining.values():
211
- remaining_hours = info["remaining_hours"]
212
- estimated_urgency = compute_estimated_urgency(
213
- remaining_hours, urgency_coefficients
284
+ # Update estimated urgency
285
+ update_urgency(
286
+ info, "estimated_urgency", urgency_estimated, urgency_coefficients, date
214
287
  )
215
- _old_estimated_urgency = info["estimated_urgency"]
216
- info["estimated_urgency"] = estimated_urgency
217
- info["urgency"] = info["urgency"] - _old_estimated_urgency + estimated_urgency
288
+ # Update due partial urgency
289
+ update_urgency(info, "due_urgency", urgency_due, urgency_coefficients, date)
290
+ # Update age partial urgency
291
+ update_urgency(info, "age_urgency", urgency_age, urgency_coefficients, date)
292
+
218
293
  started_by_user = info["task"].get("start", "") != ""
219
294
  started_by_scheduler = info["started"]
220
295
  if started_by_scheduler and not started_by_user:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: taskcheck
3
- Version: 0.3.2.post2
3
+ Version: 0.3.3
4
4
  Summary: Automatic scheduler for taskwarrior
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes