taskcheck 0.3.0__tar.gz → 0.3.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.3.0/taskcheck.egg-info → taskcheck-0.3.1}/PKG-INFO +5 -1
- {taskcheck-0.3.0 → taskcheck-0.3.1}/README.md +4 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/pyproject.toml +1 -1
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck/common.py +9 -1
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck/parallel.py +8 -0
- taskcheck-0.3.1/taskcheck/report.py +212 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1/taskcheck.egg-info}/PKG-INFO +5 -1
- taskcheck-0.3.0/taskcheck/report.py +0 -122
- {taskcheck-0.3.0 → taskcheck-0.3.1}/LICENSE +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/setup.cfg +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck/__main__.py +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck/ical.py +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck/install.py +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck/sequential.py +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck.egg-info/SOURCES.txt +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck.egg-info/dependency_links.txt +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck.egg-info/entry_points.txt +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.1}/taskcheck.egg-info/requires.txt +0 -0
- {taskcheck-0.3.0 → taskcheck-0.3.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.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Automatic scheduler for taskwarrior
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -15,6 +15,10 @@ Requires-Dist: rich>=13.9.4
|
|
|
15
15
|
|
|
16
16
|
This is a taskwarrior extension checks if tasks can be completed on time, considering estimated time and working hours.
|
|
17
17
|
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="https://github.com/user-attachments/assets/b9082701-339b-4407-b941-b613a717382c"/>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
18
22
|
## Features
|
|
19
23
|
|
|
20
24
|
- [x] Use arbitrarily complex time maps
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
This is a taskwarrior extension checks if tasks can be completed on time, considering estimated time and working hours.
|
|
5
5
|
|
|
6
|
+
<p align="center">
|
|
7
|
+
<img src="https://github.com/user-attachments/assets/b9082701-339b-4407-b941-b613a717382c"/>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
6
10
|
## Features
|
|
7
11
|
|
|
8
12
|
- [x] Use arbitrarily complex time maps
|
|
@@ -3,9 +3,12 @@ import json
|
|
|
3
3
|
from datetime import datetime, timedelta
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
import appdirs
|
|
6
|
+
from rich.console import Console
|
|
6
7
|
|
|
7
8
|
from taskcheck.ical import ical_to_dict
|
|
8
9
|
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
9
12
|
config_dir = Path(appdirs.user_config_dir("task"))
|
|
10
13
|
|
|
11
14
|
# Taskwarrior status to avoid
|
|
@@ -98,7 +101,10 @@ def pdth_to_hours(duration_str):
|
|
|
98
101
|
if "H" in duration_str:
|
|
99
102
|
hours = int(duration_str.split("T")[1].split("H")[0])
|
|
100
103
|
if "M" in duration_str:
|
|
101
|
-
|
|
104
|
+
if "H" in duration_str:
|
|
105
|
+
minutes = int(duration_str.split("H")[1].split("M")[0])
|
|
106
|
+
else:
|
|
107
|
+
minutes = int(duration_str.split("T")[1].split("M")[0])
|
|
102
108
|
return days * 24 + hours + minutes / 60
|
|
103
109
|
|
|
104
110
|
|
|
@@ -152,6 +158,8 @@ def get_long_range_time_map(
|
|
|
152
158
|
time_map = time_maps[time_map_name].get(today_weekday)
|
|
153
159
|
if time_map:
|
|
154
160
|
for schedule_start, schedule_end in time_map:
|
|
161
|
+
schedule_start = hours_to_decimal(schedule_start)
|
|
162
|
+
schedule_end = hours_to_decimal(schedule_end)
|
|
155
163
|
if schedule_start <= today_time <= schedule_end:
|
|
156
164
|
today_used_hours += today_time - schedule_start
|
|
157
165
|
break
|
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from datetime import datetime, timedelta
|
|
6
6
|
from taskcheck.common import (
|
|
7
7
|
AVOID_STATUS,
|
|
8
|
+
console,
|
|
8
9
|
get_calendars,
|
|
9
10
|
get_long_range_time_map,
|
|
10
11
|
get_tasks,
|
|
@@ -285,6 +286,13 @@ def update_tasks_with_scheduling_info(task_info, verbose):
|
|
|
285
286
|
stdout=subprocess.DEVNULL,
|
|
286
287
|
stderr=subprocess.DEVNULL,
|
|
287
288
|
)
|
|
289
|
+
due = task.get("due")
|
|
290
|
+
end_date = datetime.strptime(end_date, "%Y-%m-%d")
|
|
291
|
+
if due is not None and end_date > datetime.strptime(due, "%Y%m%dT%H%M%SZ"):
|
|
292
|
+
console.print(
|
|
293
|
+
f"[red]Warning: Task {task['id']} ('{task['description']}') is not going to be completed on time.[/red]"
|
|
294
|
+
)
|
|
295
|
+
|
|
288
296
|
if verbose:
|
|
289
297
|
print(
|
|
290
298
|
f"Updated task {task['id']} with scheduled dates {start_date} to {end_date}"
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from rich import print
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
from rich.panel import Panel
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_tasks(config, tasks, year, month, day):
|
|
17
|
+
regex = re.compile(rf"{year}-{month}-{day} - ([PDTHM0-9]+)")
|
|
18
|
+
valid_tasks = []
|
|
19
|
+
for task in tasks:
|
|
20
|
+
for line in task["scheduling"].split("\n"):
|
|
21
|
+
m = regex.match(line)
|
|
22
|
+
if m:
|
|
23
|
+
due = task.get("due", "")
|
|
24
|
+
if due:
|
|
25
|
+
due = datetime.strptime(due, "%Y%m%dT%H%M%SZ")
|
|
26
|
+
due = due - datetime.today()
|
|
27
|
+
valid_tasks.append(
|
|
28
|
+
{
|
|
29
|
+
"id": task["id"],
|
|
30
|
+
"project": task.get("project", ""),
|
|
31
|
+
"description": task["description"],
|
|
32
|
+
"scheduling_day": f"{year}-{month}-{day}",
|
|
33
|
+
"scheduling_hours": m.group(1),
|
|
34
|
+
**{
|
|
35
|
+
attr: task.get(attr, "")
|
|
36
|
+
for attr in config.get("additional_attributes", [])
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
valid_tasks = sorted(valid_tasks, key=lambda x: x["urgency"], reverse=True)
|
|
41
|
+
return valid_tasks
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_taskwarrior_date(date, _retry=True):
|
|
45
|
+
date = subprocess.run(
|
|
46
|
+
["task", "calc", date],
|
|
47
|
+
capture_output=True,
|
|
48
|
+
text=True,
|
|
49
|
+
)
|
|
50
|
+
date = date.stdout.strip()
|
|
51
|
+
try:
|
|
52
|
+
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S")
|
|
53
|
+
except Exception as _:
|
|
54
|
+
if _retry:
|
|
55
|
+
return get_taskwarrior_date("today+" + date, False)
|
|
56
|
+
else:
|
|
57
|
+
print(
|
|
58
|
+
"Please provide a valid date. Check with `task calc` that your date is in the format YYYY-MM-DDTHH:MM:SS",
|
|
59
|
+
file=sys.stderr,
|
|
60
|
+
)
|
|
61
|
+
exit(2)
|
|
62
|
+
return date
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_days_in_constraint(constraint):
|
|
66
|
+
current_date = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
67
|
+
constraint = get_taskwarrior_date(constraint)
|
|
68
|
+
while current_date <= constraint:
|
|
69
|
+
yield current_date.year, current_date.month, current_date.day
|
|
70
|
+
current_date += timedelta(days=1)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def tostring(value):
|
|
74
|
+
if isinstance(value, bool):
|
|
75
|
+
return "Yes" if value else "No"
|
|
76
|
+
elif isinstance(value, datetime):
|
|
77
|
+
return value.strftime("%Y-%m-%d %H:%M")
|
|
78
|
+
elif isinstance(value, str):
|
|
79
|
+
try:
|
|
80
|
+
date = datetime.strptime(value, "%Y%m%dT%H%M%SZ")
|
|
81
|
+
return date.strftime("%Y-%m-%d %H:%M")
|
|
82
|
+
except ValueError:
|
|
83
|
+
return value
|
|
84
|
+
else:
|
|
85
|
+
return str(value)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_unplanned_tasks(config, tasks):
|
|
89
|
+
tasks = subprocess.run(
|
|
90
|
+
["task", "scheduling:", "status:pending", "export"],
|
|
91
|
+
capture_output=True,
|
|
92
|
+
text=True,
|
|
93
|
+
)
|
|
94
|
+
tasks = json.loads(tasks.stdout)
|
|
95
|
+
return tasks
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def generate_report(config, constraint, verbose=False):
|
|
99
|
+
config = config["report"]
|
|
100
|
+
console = Console()
|
|
101
|
+
tasks = fetch_tasks()
|
|
102
|
+
|
|
103
|
+
for year, month, day in get_days_in_constraint(constraint):
|
|
104
|
+
this_day_tasks = get_tasks(config, tasks, year, month, day)
|
|
105
|
+
|
|
106
|
+
display_date_header(console, year, month, day)
|
|
107
|
+
|
|
108
|
+
display_tasks_table(console, config, this_day_tasks)
|
|
109
|
+
|
|
110
|
+
if config.get("include_unplanned"):
|
|
111
|
+
unplanned_tasks = get_unplanned_tasks(config, tasks)
|
|
112
|
+
display_unplanned_tasks(console, config, unplanned_tasks)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def fetch_tasks():
|
|
116
|
+
"""Fetch tasks from the task manager and return them as a JSON object."""
|
|
117
|
+
tasks = subprocess.run(
|
|
118
|
+
["task", "scheduling~.", "(", "+PENDING", "or", "+WAITING", ")", "export"],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
)
|
|
122
|
+
return json.loads(tasks.stdout)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def display_date_header(console, year, month, day):
|
|
126
|
+
"""Display a date header with a calendar emoji."""
|
|
127
|
+
date_str = f":calendar: [bold cyan]{year}-{month}-{day}[/bold cyan]"
|
|
128
|
+
console.print(Panel(date_str, style="bold blue", expand=False))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def display_tasks_table(console, config, tasks):
|
|
132
|
+
"""Display a table of tasks for a specific day."""
|
|
133
|
+
if tasks:
|
|
134
|
+
table = build_tasks_table(config, tasks)
|
|
135
|
+
console.print(table)
|
|
136
|
+
else:
|
|
137
|
+
console.print("[italic dim]No tasks scheduled for this day.[/italic dim]")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def build_tasks_table(config, tasks):
|
|
141
|
+
"""Build a Rich table for displaying tasks."""
|
|
142
|
+
table = Table(show_header=True, header_style="bold blue")
|
|
143
|
+
table.add_column("Task", style="dim", width=12)
|
|
144
|
+
table.add_column("Project", style="dim", width=12)
|
|
145
|
+
table.add_column("Description")
|
|
146
|
+
table.add_column("Time", justify="right")
|
|
147
|
+
for attr in config.get("additional_attributes", []):
|
|
148
|
+
table.add_column(attr.capitalize(), justify="right")
|
|
149
|
+
|
|
150
|
+
for task in tasks:
|
|
151
|
+
task_id = f"[bold green]#{task['id']}[/bold green]"
|
|
152
|
+
project = task.get("project", "")
|
|
153
|
+
description = Text(task["description"], style="white")
|
|
154
|
+
hours = f"[yellow]{task['scheduling_hours']}[/yellow]"
|
|
155
|
+
emoji = get_task_emoji(config, task)
|
|
156
|
+
|
|
157
|
+
table.add_row(
|
|
158
|
+
f"{emoji} {task_id}",
|
|
159
|
+
project,
|
|
160
|
+
description,
|
|
161
|
+
hours,
|
|
162
|
+
*[
|
|
163
|
+
tostring(task.get(attr, ""))
|
|
164
|
+
for attr in config.get("additional_attributes", [])
|
|
165
|
+
],
|
|
166
|
+
)
|
|
167
|
+
return table
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_task_emoji(config, task):
|
|
171
|
+
"""Get an emoji based on keywords in the task description."""
|
|
172
|
+
for keyword in config.get("emoji_keywords", []):
|
|
173
|
+
if keyword in task["description"].lower():
|
|
174
|
+
return config["emoji_keywords"][keyword]
|
|
175
|
+
return ":pushpin:"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def display_unplanned_tasks(console, config, tasks):
|
|
179
|
+
"""Display unplanned tasks if any are found."""
|
|
180
|
+
if tasks:
|
|
181
|
+
table = build_unplanned_tasks_table(config, tasks)
|
|
182
|
+
console.print(Panel("Unplanned Tasks", style="bold blue", expand=False))
|
|
183
|
+
console.print(table)
|
|
184
|
+
else:
|
|
185
|
+
console.print(
|
|
186
|
+
Panel(
|
|
187
|
+
"[italic dim]No unplanned tasks found.[/italic dim]",
|
|
188
|
+
style="bold blue",
|
|
189
|
+
expand=False,
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def build_unplanned_tasks_table(config, tasks):
|
|
195
|
+
"""Build a Rich table for displaying unplanned tasks."""
|
|
196
|
+
table = Table(show_header=True, header_style="bold blue")
|
|
197
|
+
table.add_column("Task", style="dim", width=12)
|
|
198
|
+
table.add_column("Project", style="dim", width=12)
|
|
199
|
+
table.add_column("Description")
|
|
200
|
+
|
|
201
|
+
for task in tasks:
|
|
202
|
+
task_id = f"[bold green]#{task['id']}[/bold green]"
|
|
203
|
+
project = task.get("project", "")
|
|
204
|
+
description = Text(task["description"], style="white")
|
|
205
|
+
emoji = get_task_emoji(config, task)
|
|
206
|
+
|
|
207
|
+
table.add_row(
|
|
208
|
+
f"{emoji} {task_id}",
|
|
209
|
+
project,
|
|
210
|
+
description,
|
|
211
|
+
)
|
|
212
|
+
return table
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: taskcheck
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Automatic scheduler for taskwarrior
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -15,6 +15,10 @@ Requires-Dist: rich>=13.9.4
|
|
|
15
15
|
|
|
16
16
|
This is a taskwarrior extension checks if tasks can be completed on time, considering estimated time and working hours.
|
|
17
17
|
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="https://github.com/user-attachments/assets/b9082701-339b-4407-b941-b613a717382c"/>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
18
22
|
## Features
|
|
19
23
|
|
|
20
24
|
- [x] Use arbitrarily complex time maps
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from datetime import timedelta
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
import json
|
|
5
|
-
import re
|
|
6
|
-
import subprocess
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from rich import print
|
|
10
|
-
from rich.console import Console
|
|
11
|
-
from rich.table import Table
|
|
12
|
-
from rich.text import Text
|
|
13
|
-
from rich.panel import Panel
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def get_tasks(tasks, year, month, day):
|
|
17
|
-
regex = re.compile(rf"{year}-{month}-{day} - ([PDTHM0-9]+)")
|
|
18
|
-
valid_tasks = []
|
|
19
|
-
for task in tasks:
|
|
20
|
-
for line in task["scheduling"].split("\n"):
|
|
21
|
-
m = regex.match(line)
|
|
22
|
-
if m:
|
|
23
|
-
due = task.get("due", "")
|
|
24
|
-
if due:
|
|
25
|
-
due = datetime.strptime(due, "%Y%m%dT%H%M%SZ")
|
|
26
|
-
due = due - datetime.today()
|
|
27
|
-
valid_tasks.append(
|
|
28
|
-
{
|
|
29
|
-
"id": task["id"],
|
|
30
|
-
"project": task.get("project", ""),
|
|
31
|
-
"description": task["description"],
|
|
32
|
-
"scheduling_day": f"{year}-{month}-{day}",
|
|
33
|
-
"scheduling_hours": m.group(1),
|
|
34
|
-
"urgency": task["urgency"],
|
|
35
|
-
"due": due,
|
|
36
|
-
}
|
|
37
|
-
)
|
|
38
|
-
valid_tasks = sorted(valid_tasks, key=lambda x: x["urgency"], reverse=True)
|
|
39
|
-
return valid_tasks
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def get_taskwarrior_date(date, _retry=True):
|
|
43
|
-
date = subprocess.run(
|
|
44
|
-
["task", "calc", date],
|
|
45
|
-
capture_output=True,
|
|
46
|
-
text=True,
|
|
47
|
-
)
|
|
48
|
-
date = date.stdout.strip()
|
|
49
|
-
try:
|
|
50
|
-
date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S")
|
|
51
|
-
except Exception as _:
|
|
52
|
-
if _retry:
|
|
53
|
-
return get_taskwarrior_date("today+" + date, False)
|
|
54
|
-
else:
|
|
55
|
-
print(
|
|
56
|
-
"Please provide a valid date. Check with `task calc` that your date is in the format YYYY-MM-DDTHH:MM:SS",
|
|
57
|
-
file=sys.stderr,
|
|
58
|
-
)
|
|
59
|
-
exit(2)
|
|
60
|
-
return date
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def get_days_in_constraint(constraint):
|
|
64
|
-
current_date = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
65
|
-
constraint = get_taskwarrior_date(constraint)
|
|
66
|
-
while current_date <= constraint:
|
|
67
|
-
yield current_date.year, current_date.month, current_date.day
|
|
68
|
-
current_date += timedelta(days=1)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def generate_report(config, constraint, verbose=False):
|
|
72
|
-
console = Console()
|
|
73
|
-
tasks = subprocess.run(
|
|
74
|
-
["task", "scheduling~.", "export"], capture_output=True, text=True
|
|
75
|
-
)
|
|
76
|
-
tasks = json.loads(tasks.stdout)
|
|
77
|
-
|
|
78
|
-
for year, month, day in get_days_in_constraint(constraint):
|
|
79
|
-
this_day_tasks = get_tasks(tasks, year, month, day)
|
|
80
|
-
|
|
81
|
-
# Create a colorful date header with emojis and a panel
|
|
82
|
-
date_str = f":calendar: [bold cyan]{year}-{month}-{day}[/bold cyan]"
|
|
83
|
-
console.print(Panel(date_str, style="bold magenta", expand=False))
|
|
84
|
-
|
|
85
|
-
if this_day_tasks:
|
|
86
|
-
table = Table(show_header=True, header_style="bold blue")
|
|
87
|
-
table.add_column("Task", style="dim", width=12)
|
|
88
|
-
table.add_column("Project", style="dim", width=12)
|
|
89
|
-
table.add_column("Description")
|
|
90
|
-
table.add_column("Time", justify="right")
|
|
91
|
-
table.add_column("Urgency")
|
|
92
|
-
table.add_column("Due date")
|
|
93
|
-
|
|
94
|
-
for task in this_day_tasks:
|
|
95
|
-
task_id = f"[bold green]#{task['id']}[/bold green]"
|
|
96
|
-
project = task["project"]
|
|
97
|
-
description = Text(task["description"], style="white")
|
|
98
|
-
hours = f"[yellow]{task['scheduling_hours']}h[/yellow]"
|
|
99
|
-
urgency = f"[bold]{task['urgency']}[/bold]"
|
|
100
|
-
due = task["due"]
|
|
101
|
-
if due != "" and due < timedelta(days=5):
|
|
102
|
-
due = f"[red]{due.days}d[/red]"
|
|
103
|
-
elif due != "":
|
|
104
|
-
due = f"[green]{due.days}d[/green]"
|
|
105
|
-
|
|
106
|
-
# Add an emoji based on a keyword in the description
|
|
107
|
-
if "meeting" in task["description"].lower():
|
|
108
|
-
emoji = ":busts_in_silhouette:"
|
|
109
|
-
elif "review" in task["description"].lower():
|
|
110
|
-
emoji = ":mag_right:"
|
|
111
|
-
elif "write" in task["description"].lower():
|
|
112
|
-
emoji = ":pencil:"
|
|
113
|
-
else:
|
|
114
|
-
emoji = ":pushpin:"
|
|
115
|
-
|
|
116
|
-
table.add_row(
|
|
117
|
-
f"{emoji} {task_id}", project, description, hours, urgency, due
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
console.print(table)
|
|
121
|
-
else:
|
|
122
|
-
console.print("[italic dim]No tasks scheduled for this day.[/italic dim]")
|
|
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
|