taskpulse-24 0.1.0__py3-none-any.whl
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.
- taskpulse/__init__.py +8 -0
- taskpulse/cli.py +51 -0
- taskpulse/models.py +41 -0
- taskpulse/planner.py +50 -0
- taskpulse_24-0.1.0.dist-info/METADATA +153 -0
- taskpulse_24-0.1.0.dist-info/RECORD +10 -0
- taskpulse_24-0.1.0.dist-info/WHEEL +5 -0
- taskpulse_24-0.1.0.dist-info/entry_points.txt +2 -0
- taskpulse_24-0.1.0.dist-info/licenses/LICENSE +22 -0
- taskpulse_24-0.1.0.dist-info/top_level.txt +1 -0
taskpulse/__init__.py
ADDED
taskpulse/cli.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Command-line interface for quick planning from CSV input."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import csv
|
|
7
|
+
from datetime import date
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from .models import Priority, Task
|
|
11
|
+
from .planner import schedule
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _read_tasks(path: Path) -> list[Task]:
|
|
15
|
+
with path.open(newline="", encoding="utf-8") as source:
|
|
16
|
+
rows = csv.DictReader(source)
|
|
17
|
+
required = {"title", "minutes", "priority"}
|
|
18
|
+
if not required.issubset(rows.fieldnames or set()):
|
|
19
|
+
raise ValueError("CSV must contain title, minutes, and priority columns")
|
|
20
|
+
tasks = []
|
|
21
|
+
for row in rows:
|
|
22
|
+
due = date.fromisoformat(row["due"]) if row.get("due") else None
|
|
23
|
+
tasks.append(
|
|
24
|
+
Task(
|
|
25
|
+
title=row["title"],
|
|
26
|
+
minutes=int(row["minutes"]),
|
|
27
|
+
priority=Priority(int(row["priority"])),
|
|
28
|
+
due=due,
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
return tasks
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main() -> None:
|
|
35
|
+
parser = argparse.ArgumentParser(description="Build a focused task schedule.")
|
|
36
|
+
parser.add_argument("csv_file", type=Path, help="CSV with title, minutes, priority, due")
|
|
37
|
+
parser.add_argument("--daily-minutes", type=int, default=120)
|
|
38
|
+
args = parser.parse_args()
|
|
39
|
+
|
|
40
|
+
for day, tasks in schedule(
|
|
41
|
+
_read_tasks(args.csv_file),
|
|
42
|
+
daily_minutes=args.daily_minutes,
|
|
43
|
+
).items():
|
|
44
|
+
print(day.isoformat())
|
|
45
|
+
for task in tasks:
|
|
46
|
+
print(f" - {task.title} ({task.minutes} min, priority {task.priority.name})")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
main()
|
|
51
|
+
|
taskpulse/models.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Public data models for TaskPulse."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import date
|
|
7
|
+
from enum import IntEnum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Priority(IntEnum):
|
|
11
|
+
"""User-facing priority levels."""
|
|
12
|
+
|
|
13
|
+
LOW = 1
|
|
14
|
+
MEDIUM = 2
|
|
15
|
+
HIGH = 3
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class Task:
|
|
20
|
+
"""A task that can be ranked and scheduled.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
title: Short, human-readable task name.
|
|
24
|
+
minutes: Expected focused work time; must be positive.
|
|
25
|
+
priority: Importance from 1 (low) to 3 (high).
|
|
26
|
+
due: Optional due date. Earlier dates receive more urgency.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
title: str
|
|
30
|
+
minutes: int
|
|
31
|
+
priority: Priority = Priority.MEDIUM
|
|
32
|
+
due: date | None = None
|
|
33
|
+
|
|
34
|
+
def __post_init__(self) -> None:
|
|
35
|
+
if not self.title.strip():
|
|
36
|
+
raise ValueError("title must not be empty")
|
|
37
|
+
if self.minutes <= 0:
|
|
38
|
+
raise ValueError("minutes must be greater than zero")
|
|
39
|
+
if not isinstance(self.priority, Priority):
|
|
40
|
+
raise TypeError("priority must be a Priority value")
|
|
41
|
+
|
taskpulse/planner.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Task ranking and schedule generation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import date, datetime, timedelta
|
|
6
|
+
|
|
7
|
+
from .models import Task
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _score(task: Task, today: date) -> tuple[int, int, int]:
|
|
11
|
+
"""Return a sortable score; larger values represent more urgency."""
|
|
12
|
+
due_score = 0 if task.due is None else max(0, 30 - (task.due - today).days)
|
|
13
|
+
return (int(task.priority) * 100 + due_score, -task.minutes, -len(task.title))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def prioritize(tasks: list[Task], *, today: date | None = None) -> list[Task]:
|
|
17
|
+
"""Return tasks from most to least urgent without mutating the input."""
|
|
18
|
+
reference = today or datetime.now().astimezone().date()
|
|
19
|
+
return sorted(tasks, key=lambda task: _score(task, reference), reverse=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def schedule(
|
|
23
|
+
tasks: list[Task],
|
|
24
|
+
*,
|
|
25
|
+
daily_minutes: int,
|
|
26
|
+
start: date | None = None,
|
|
27
|
+
) -> dict[date, list[Task]]:
|
|
28
|
+
"""Pack prioritized tasks into consecutive workdays.
|
|
29
|
+
|
|
30
|
+
A task is never split. If one task exceeds ``daily_minutes``, it receives
|
|
31
|
+
a day of its own so the function always makes progress.
|
|
32
|
+
"""
|
|
33
|
+
if daily_minutes <= 0:
|
|
34
|
+
raise ValueError("daily_minutes must be greater than zero")
|
|
35
|
+
|
|
36
|
+
start_date = start or datetime.now().astimezone().date()
|
|
37
|
+
result: dict[date, list[Task]] = {}
|
|
38
|
+
remaining = daily_minutes
|
|
39
|
+
day = start_date
|
|
40
|
+
|
|
41
|
+
for task in prioritize(tasks, today=start_date):
|
|
42
|
+
if result.get(day) and task.minutes > remaining:
|
|
43
|
+
day += timedelta(days=1)
|
|
44
|
+
remaining = daily_minutes
|
|
45
|
+
result.setdefault(day, []).append(task)
|
|
46
|
+
remaining -= task.minutes
|
|
47
|
+
if remaining <= 0:
|
|
48
|
+
day += timedelta(days=1)
|
|
49
|
+
remaining = daily_minutes
|
|
50
|
+
return result
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: taskpulse-24
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A small, dependency-free task prioritizer and daily planner.
|
|
5
|
+
Author: TaskPulse contributors
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 TaskPulse contributors
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Project-URL: Homepage, https://github.com/OttoHui/taskpulse
|
|
30
|
+
Project-URL: Repository, https://github.com/OttoHui/taskpulse
|
|
31
|
+
Project-URL: Documentation, https://github.com/OttoHui/taskpulse#readme
|
|
32
|
+
Keywords: productivity,planning,tasks,scheduling
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Intended Audience :: Education
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
39
|
+
Requires-Python: >=3.10
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
License-File: LICENSE
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
44
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# TaskPulse
|
|
48
|
+
|
|
49
|
+
TaskPulse is a small, dependency-free Python package that turns a task list
|
|
50
|
+
into a focused daily plan. It is designed for students and small project teams
|
|
51
|
+
who want transparent prioritization without sending personal data to a service.
|
|
52
|
+
|
|
53
|
+
## Project team
|
|
54
|
+
|
|
55
|
+
**Group 24 (6/6 members)**
|
|
56
|
+
|
|
57
|
+
- Tsz Hei CHUI
|
|
58
|
+
- Fong Kwan HUI
|
|
59
|
+
- David KWONG
|
|
60
|
+
- Sik Man LAM
|
|
61
|
+
- Hong Shing WONG
|
|
62
|
+
- Chun Hin YIU
|
|
63
|
+
|
|
64
|
+
## Why it exists
|
|
65
|
+
|
|
66
|
+
Most task apps either require a hosted account or hide prioritization behind a
|
|
67
|
+
complex interface. TaskPulse keeps the algorithm inspectable:
|
|
68
|
+
|
|
69
|
+
1. importance is explicit (`LOW`, `MEDIUM`, or `HIGH`);
|
|
70
|
+
2. approaching due dates add urgency;
|
|
71
|
+
3. shorter tasks win ties, making plans easier to finish;
|
|
72
|
+
4. tasks are packed into days without splitting a task across sessions.
|
|
73
|
+
|
|
74
|
+
The result is useful for study plans, sprint preparation, and a one-minute
|
|
75
|
+
project demonstration.
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install taskpulse-24
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For local development:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git clone https://github.com/OttoHui/taskpulse.git
|
|
87
|
+
cd taskpulse
|
|
88
|
+
pip install -e ".[dev]" # or: pip install -e .
|
|
89
|
+
pytest
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Python API
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from datetime import date
|
|
96
|
+
from taskpulse import Priority, Task, schedule
|
|
97
|
+
|
|
98
|
+
tasks = [
|
|
99
|
+
Task("Write project abstract", 45, Priority.HIGH, date(2026, 10, 10)),
|
|
100
|
+
Task("Review references", 30, Priority.MEDIUM),
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
plan = schedule(tasks, daily_minutes=60, start=date(2026, 10, 7))
|
|
104
|
+
for day, items in plan.items():
|
|
105
|
+
print(day, [item.title for item in items])
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Command line
|
|
109
|
+
|
|
110
|
+
Create `tasks.csv`:
|
|
111
|
+
|
|
112
|
+
```csv
|
|
113
|
+
title,minutes,priority,due
|
|
114
|
+
Write project abstract,45,3,2026-10-10
|
|
115
|
+
Review references,30,2,
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Then run:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
taskpulse tasks.csv --daily-minutes 60
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Project and collaboration
|
|
125
|
+
|
|
126
|
+
Suggested team split for Group 24:
|
|
127
|
+
|
|
128
|
+
- **Tsz Hei CHUI:** project coordination, requirements, and final integration.
|
|
129
|
+
- **Fong Kwan HUI:** core models, prioritization, and scheduling logic.
|
|
130
|
+
- **David KWONG:** command-line interface and user experience.
|
|
131
|
+
- **Sik Man LAM:** tests, edge cases, and quality checks.
|
|
132
|
+
- **Hong Shing WONG:** documentation, examples, and PyPI metadata.
|
|
133
|
+
- **Chun Hin YIU:** demo, presentation wiki, and release verification.
|
|
134
|
+
|
|
135
|
+
Make small commits by feature and review each other's pull requests. The
|
|
136
|
+
published distribution is named `taskpulse-24`; its import name remains
|
|
137
|
+
`taskpulse`.
|
|
138
|
+
|
|
139
|
+
## One-minute demonstration
|
|
140
|
+
|
|
141
|
+
1. Show the two-row CSV (10 seconds).
|
|
142
|
+
2. Run `taskpulse tasks.csv --daily-minutes 60` (15 seconds).
|
|
143
|
+
3. Explain that priority and due date determine order, while capacity determines
|
|
144
|
+
the day (20 seconds).
|
|
145
|
+
4. Show the equivalent Python API and mention that no external service or
|
|
146
|
+
dependency is required (15 seconds).
|
|
147
|
+
|
|
148
|
+
## Roadmap
|
|
149
|
+
|
|
150
|
+
- optional weekend skipping;
|
|
151
|
+
- JSON input/output;
|
|
152
|
+
- calendar export;
|
|
153
|
+
- configurable scoring strategies.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
taskpulse/__init__.py,sha256=pqMkZ2VhM76IVkdqX6HelgF78veo2DKXWOmFtFYtTjc,222
|
|
2
|
+
taskpulse/cli.py,sha256=PlV3mIOak2DMcCZTFWYONdki8FddP8uD8AvOtU4WChE,1595
|
|
3
|
+
taskpulse/models.py,sha256=83TR0Iig8LzkoBxwHEC7BkOcd8lRq0qVpJs41jOxo3w,1065
|
|
4
|
+
taskpulse/planner.py,sha256=Sl_ctNFO-Lq9IBlMseY6MTnQp9EJGFmGvM7w5QylfLg,1722
|
|
5
|
+
taskpulse_24-0.1.0.dist-info/licenses/LICENSE,sha256=jweqFtKfuouBWmwQ4nfklb9PZkUSScQ5amnvQvRSiz0,1080
|
|
6
|
+
taskpulse_24-0.1.0.dist-info/METADATA,sha256=gEVqs8XafUBY26hTuTJC_AcJ0hYWxPBfFjVONaMaqrs,4934
|
|
7
|
+
taskpulse_24-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
8
|
+
taskpulse_24-0.1.0.dist-info/entry_points.txt,sha256=dPyNr4a6m5jIak-Fs84CHUGhge-qdCOKTOaXXTwBkUI,49
|
|
9
|
+
taskpulse_24-0.1.0.dist-info/top_level.txt,sha256=QONlJ8x0qyDyaGCiNuEg4IBDtm68HD3GLCQ98RCuxKE,10
|
|
10
|
+
taskpulse_24-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TaskPulse contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
taskpulse
|