mdtask 0.1.0__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.
- mdtask-0.1.0/PKG-INFO +7 -0
- mdtask-0.1.0/pyproject.toml +16 -0
- mdtask-0.1.0/setup.cfg +4 -0
- mdtask-0.1.0/src/mdtask/__init__.py +0 -0
- mdtask-0.1.0/src/mdtask/cli.py +192 -0
- mdtask-0.1.0/src/mdtask.egg-info/PKG-INFO +7 -0
- mdtask-0.1.0/src/mdtask.egg-info/SOURCES.txt +9 -0
- mdtask-0.1.0/src/mdtask.egg-info/dependency_links.txt +1 -0
- mdtask-0.1.0/src/mdtask.egg-info/entry_points.txt +2 -0
- mdtask-0.1.0/src/mdtask.egg-info/requires.txt +2 -0
- mdtask-0.1.0/src/mdtask.egg-info/top_level.txt +1 -0
mdtask-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mdtask"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Markdown task list CLI"
|
|
5
|
+
authors = [{name="Your Name"}]
|
|
6
|
+
dependencies = [
|
|
7
|
+
"typer",
|
|
8
|
+
"rich"
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[project.scripts]
|
|
12
|
+
task = "mdtask.cli:main"
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["setuptools"]
|
|
16
|
+
build-backend = "setuptools.build_meta"
|
mdtask-0.1.0/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import typer
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from rich import print
|
|
5
|
+
from datetime import date
|
|
6
|
+
import uuid
|
|
7
|
+
app = typer.Typer()
|
|
8
|
+
|
|
9
|
+
TASK_PATTERN = re.compile(r"^(\s*)- \[( |x)\] (.*)")
|
|
10
|
+
DUE_PATTERN = re.compile(r"due:(\d{4}-\d{2}-\d{2})")
|
|
11
|
+
ID_PATTERN = re.compile(r"\bid:([0-9a-f]{6})\b")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_id():
|
|
16
|
+
return uuid.uuid4().hex[:6]
|
|
17
|
+
class Task:
|
|
18
|
+
def __init__(self, indent, done, text, line_no):
|
|
19
|
+
self.indent = indent
|
|
20
|
+
self.done = done
|
|
21
|
+
self.text = text
|
|
22
|
+
self.line_no = line_no
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
status = "✓" if self.done else " "
|
|
26
|
+
return f"{self.line_no:03} [{status}] {self.text}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def parse_tasks(file_path):
|
|
30
|
+
tasks = []
|
|
31
|
+
path = Path(file_path)
|
|
32
|
+
if not path.exists():
|
|
33
|
+
return [], []
|
|
34
|
+
lines = path.read_text().splitlines()
|
|
35
|
+
|
|
36
|
+
for i, line in enumerate(lines):
|
|
37
|
+
m = TASK_PATTERN.match(line)
|
|
38
|
+
if m:
|
|
39
|
+
indent, done, text = m.groups()
|
|
40
|
+
tasks.append(Task(len(indent), done == "x", text, i))
|
|
41
|
+
|
|
42
|
+
return tasks, lines
|
|
43
|
+
|
|
44
|
+
def find_task_by_id(tasks, task_id):
|
|
45
|
+
for t in tasks:
|
|
46
|
+
m = ID_PATTERN.search(t.text)
|
|
47
|
+
if m and m.group(1) == task_id:
|
|
48
|
+
return t
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_due_date(text):
|
|
53
|
+
m = DUE_PATTERN.search(text)
|
|
54
|
+
if not m:
|
|
55
|
+
return None
|
|
56
|
+
return date.fromisoformat(m.group(1))
|
|
57
|
+
|
|
58
|
+
@app.command()
|
|
59
|
+
def list(file: str = "tasks.md"):
|
|
60
|
+
"""List tasks"""
|
|
61
|
+
tasks, _ = parse_tasks(file)
|
|
62
|
+
|
|
63
|
+
for t in tasks:
|
|
64
|
+
status = "[green]✓[/green]" if t.done else "[red]•[/red]"
|
|
65
|
+
print(f"{t.line_no:03}L {status} {t.text}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@app.command()
|
|
69
|
+
def add(text: str, file: str = "tasks.md"):
|
|
70
|
+
"""Add new task"""
|
|
71
|
+
path = Path(file)
|
|
72
|
+
if not path.exists():
|
|
73
|
+
path.touch()
|
|
74
|
+
content = path.read_text()
|
|
75
|
+
task_id = generate_id()
|
|
76
|
+
|
|
77
|
+
new_task = f"- [ ] {text} id:{task_id}\n"
|
|
78
|
+
path.write_text(content + "\n" + new_task)
|
|
79
|
+
|
|
80
|
+
print("[green]Task added[/green]")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@app.command()
|
|
84
|
+
def done(task_id: str, file: str = "tasks.md"):
|
|
85
|
+
"""Mark task as done by id"""
|
|
86
|
+
tasks, lines = parse_tasks(file)
|
|
87
|
+
t = find_task_by_id(tasks, task_id)
|
|
88
|
+
if not t:
|
|
89
|
+
print("[red]Task not found[/red]")
|
|
90
|
+
return
|
|
91
|
+
if t.done:
|
|
92
|
+
print("[yellow]Task already completed[/yellow]")
|
|
93
|
+
return
|
|
94
|
+
lines[t.line_no] = re.sub(r"\[ \]", "[x]", lines[t.line_no])
|
|
95
|
+
Path(file).write_text("\n".join(lines))
|
|
96
|
+
print("[green]Task completed[/green]")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.command()
|
|
100
|
+
def edit(task_id: str, text: str, file: str = "tasks.md"):
|
|
101
|
+
"""Edit task text by id"""
|
|
102
|
+
tasks, lines = parse_tasks(file)
|
|
103
|
+
t = find_task_by_id(tasks, task_id)
|
|
104
|
+
if not t:
|
|
105
|
+
print("[red]Task not found[/red]")
|
|
106
|
+
return
|
|
107
|
+
checkbox = "[x]" if t.done else "[ ]"
|
|
108
|
+
indent = " " * t.indent
|
|
109
|
+
lines[t.line_no] = f"{indent}- {checkbox} {text} id:{task_id}"
|
|
110
|
+
Path(file).write_text("\n".join(lines))
|
|
111
|
+
print("[green]Task updated[/green]")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@app.command()
|
|
115
|
+
def remove(task_id: str, file: str = "tasks.md"):
|
|
116
|
+
"""Remove task by id"""
|
|
117
|
+
tasks, lines = parse_tasks(file)
|
|
118
|
+
t = find_task_by_id(tasks, task_id)
|
|
119
|
+
if not t:
|
|
120
|
+
print("[red]Task not found[/red]")
|
|
121
|
+
return
|
|
122
|
+
lines.pop(t.line_no)
|
|
123
|
+
Path(file).write_text("\n".join(lines))
|
|
124
|
+
print("[green]Task removed[/green]")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@app.command()
|
|
128
|
+
def tag(tag: str, file: str = "tasks.md"):
|
|
129
|
+
"""Filter tasks by tag"""
|
|
130
|
+
tasks, _ = parse_tasks(file)
|
|
131
|
+
|
|
132
|
+
for t in tasks:
|
|
133
|
+
if tag in t.text:
|
|
134
|
+
print(t)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@app.command()
|
|
138
|
+
def due(date: str, file: str = "tasks.md"):
|
|
139
|
+
"""Filter tasks by due date"""
|
|
140
|
+
tasks, _ = parse_tasks(file)
|
|
141
|
+
|
|
142
|
+
for t in tasks:
|
|
143
|
+
if f"due:{date}" in t.text:
|
|
144
|
+
print(t)
|
|
145
|
+
|
|
146
|
+
@app.command()
|
|
147
|
+
def today(file: str = "tasks.md"):
|
|
148
|
+
"""Show tasks due today"""
|
|
149
|
+
|
|
150
|
+
tasks, _ = parse_tasks(file)
|
|
151
|
+
today = date.today()
|
|
152
|
+
|
|
153
|
+
for t in tasks:
|
|
154
|
+
due = get_due_date(t.text)
|
|
155
|
+
if due == today:
|
|
156
|
+
status = "[green]✓[/green]" if t.done else "[red]•[/red]"
|
|
157
|
+
print(f"{t.line_no:03} {status} {t.text}")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@app.command()
|
|
161
|
+
def tomorrow(file: str = "tasks.md"):
|
|
162
|
+
"""Show tasks due tomorrow"""
|
|
163
|
+
|
|
164
|
+
tasks, _ = parse_tasks(file)
|
|
165
|
+
target = date.today() + timedelta(days=1)
|
|
166
|
+
|
|
167
|
+
for t in tasks:
|
|
168
|
+
due = get_due_date(t.text)
|
|
169
|
+
if due == target:
|
|
170
|
+
status = "[green]✓[/green]" if t.done else "[red]•[/red]"
|
|
171
|
+
print(f"{t.line_no:03} {status} {t.text}")
|
|
172
|
+
|
|
173
|
+
from datetime import timedelta
|
|
174
|
+
|
|
175
|
+
@app.command()
|
|
176
|
+
def week(file: str = "tasks.md"):
|
|
177
|
+
"""Show tasks due within the next 7 days"""
|
|
178
|
+
|
|
179
|
+
tasks, _ = parse_tasks(file)
|
|
180
|
+
|
|
181
|
+
today = date.today()
|
|
182
|
+
end = today + timedelta(days=7)
|
|
183
|
+
|
|
184
|
+
for t in tasks:
|
|
185
|
+
due = get_due_date(t.text)
|
|
186
|
+
|
|
187
|
+
if due and today <= due <= end:
|
|
188
|
+
status = "[green]✓[/green]" if t.done else "[red]•[/red]"
|
|
189
|
+
print(f"{t.line_no:03} {status} {t.text}")
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
app()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/mdtask/__init__.py
|
|
3
|
+
src/mdtask/cli.py
|
|
4
|
+
src/mdtask.egg-info/PKG-INFO
|
|
5
|
+
src/mdtask.egg-info/SOURCES.txt
|
|
6
|
+
src/mdtask.egg-info/dependency_links.txt
|
|
7
|
+
src/mdtask.egg-info/entry_points.txt
|
|
8
|
+
src/mdtask.egg-info/requires.txt
|
|
9
|
+
src/mdtask.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mdtask
|