kanban-python 0.3.7__tar.gz → 0.3.9__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.
- {kanban-python-0.3.7 → kanban-python-0.3.9}/CHANGELOG.md +10 -0
- {kanban-python-0.3.7/src/kanban_python.egg-info → kanban-python-0.3.9}/PKG-INFO +9 -6
- {kanban-python-0.3.7 → kanban-python-0.3.9}/README.md +7 -5
- kanban-python-0.3.9/images/image_header.PNG +0 -0
- kanban-python-0.3.9/images/image_kanban.PNG +0 -0
- kanban-python-0.3.9/images/image_kanban_change.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/setup.cfg +1 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/constants.py +2 -1
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/controls.py +3 -1
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/interface.py +81 -26
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/utils.py +38 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9/src/kanban_python.egg-info}/PKG-INFO +9 -6
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python.egg-info/requires.txt +1 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/tests/conftest.py +1 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/tests/test_utils.py +71 -4
- kanban-python-0.3.7/images/image_header.PNG +0 -0
- kanban-python-0.3.7/images/image_kanban.PNG +0 -0
- kanban-python-0.3.7/images/image_kanban_change.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/.coveragerc +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/.github/workflows/ci.yml +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/.gitignore +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/.isort.cfg +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/.pre-commit-config.yaml +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/.readthedocs.yml +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/AUTHORS.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/CONTRIBUTING.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/LICENSE.txt +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/Makefile +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/_static/.gitignore +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/authors.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/changelog.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/conf.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/contributing.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/index.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/license.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/readme.md +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/docs/requirements.txt +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_config.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_kanban_configure.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_kanban_init.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_kanban_report.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_kanban_report_document.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_scan_table.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_scan_view.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/images/image_task_example.PNG +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/pyproject.toml +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/setup.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/__init__.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/app.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/cli_parser.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python/config.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python.egg-info/SOURCES.txt +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python.egg-info/dependency_links.txt +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python.egg-info/entry_points.txt +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python.egg-info/not-zip-safe +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/src/kanban_python.egg-info/top_level.txt +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/tests/test_config.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/tests/test_interface.py +0 -0
- {kanban-python-0.3.7 → kanban-python-0.3.9}/tox.ini +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Version 0.3.9
|
|
4
|
+
- Add `from __future__ import annotations` to controls.py to not error on Union TypeHint if python version is <=3.9
|
|
5
|
+
|
|
6
|
+
## Version 0.3.8
|
|
7
|
+
- Add `Due_Date` to Task Structure
|
|
8
|
+
- App now asks for `Due_Date` on task creation and task update
|
|
9
|
+
- Show `days left` on board for task with `Due_Date`
|
|
10
|
+
- Show `days left` of most overdue or urgent task for each board when changing boards
|
|
11
|
+
- Add `freezegun` as test requirement
|
|
12
|
+
|
|
3
13
|
## Version 0.3.7
|
|
4
14
|
- Bug Fix, `Complete_Time`-Key was wrong in Task Creation, when task was created with `kanban scan`
|
|
5
15
|
- Add new function `kanban report` to show a github contribution like visual + creating a `.md` report
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kanban-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.9
|
|
4
4
|
Summary: Terminal Kanban App written in Python
|
|
5
5
|
Home-page: https://github.com/Zaloog/kanban-python
|
|
6
6
|
Author: Zaloog
|
|
@@ -19,6 +19,7 @@ Requires-Dist: platformdirs<4,>=3
|
|
|
19
19
|
Provides-Extra: testing
|
|
20
20
|
Requires-Dist: setuptools; extra == "testing"
|
|
21
21
|
Requires-Dist: pytest; extra == "testing"
|
|
22
|
+
Requires-Dist: freezegun; extra == "testing"
|
|
22
23
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
23
24
|
|
|
24
25
|
<!-- These are examples of badges you might want to add to your README:
|
|
@@ -35,7 +36,7 @@ Requires-Dist: pytest-cov; extra == "testing"
|
|
|
35
36
|
[](https://pyscaffold.org/)
|
|
36
37
|
[](https://pypi.org/project/kanban-python/)
|
|
37
38
|
[](https://pepy.tech/project/kanban-python)
|
|
38
|
-
[](https://coveralls.io/github/Zaloog/kanban-python?branch=refs/tags/v0.3.9)
|
|
39
40
|
# kanban-python
|
|
40
41
|
|
|
41
42
|
> A Terminal Kanban Application written in Python to boost your productivity :rocket:
|
|
@@ -58,7 +59,9 @@ It was a great help for developing my first package and I can highly recommend i
|
|
|
58
59
|
<details><summary>Colorful and Interactive</summary>
|
|
59
60
|
|
|
60
61
|
- kanban-python uses [rich] under the hood to process user input
|
|
61
|
-
and display nice looking
|
|
62
|
+
and display nice looking kanban-boards to the terminal.
|
|
63
|
+
- Each task has a unique `ID` per board and also has an optional `Tag` and `Due Date` associated with it,
|
|
64
|
+
which are displayed alongside its `Title`
|
|
62
65
|
|
|
63
66
|
</details>
|
|
64
67
|
|
|
@@ -100,7 +103,7 @@ This can be edited manually or within the kanban-python application. It tracks t
|
|
|
100
103
|
|
|
101
104
|
- Each created board comes with its own name and `pykanban.json` file,
|
|
102
105
|
which stores all tasks for that board. The files are stored in board specific folders under `$USER_DATA_DIR/kanban-python/kanban_boards/<BOARDNAME>`.
|
|
103
|
-
When changing Boards you also get an overview over tasks in visible columns for each board.
|
|
106
|
+
When changing Boards you also get an overview over tasks in visible columns for each board and the most urgent or overdue task on that board.
|
|
104
107
|

|
|
105
108
|
|
|
106
109
|
</details>
|
|
@@ -129,7 +132,7 @@ with `kanban configure`.
|
|
|
129
132
|
|
|
130
133
|
- When you use [kanban report](#create-report) a github-like contribution map is displayed for the current year,
|
|
131
134
|
Also a markdown file is created with all tasks comleted based on the moment, when the tasks were moved to Done Column.
|
|
132
|
-

|
|
133
136
|
|
|
134
137
|
</details>
|
|
135
138
|
|
|
@@ -179,7 +182,7 @@ The filepath were the task was found will be added as description of the task.
|
|
|
179
182
|
```
|
|
180
183
|
Goes over all your Boards and creates a single markdown file by checking the `Completion Dates` of your tasks.
|
|
181
184
|
Also shows a nice github-like contribution table for the current year.
|
|
182
|
-

|
|
183
186
|
|
|
184
187
|
### Change Settings
|
|
185
188
|
```bash
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://pyscaffold.org/)
|
|
13
13
|
[](https://pypi.org/project/kanban-python/)
|
|
14
14
|
[](https://pepy.tech/project/kanban-python)
|
|
15
|
-
[](https://coveralls.io/github/Zaloog/kanban-python?branch=refs/tags/v0.3.9)
|
|
16
16
|
# kanban-python
|
|
17
17
|
|
|
18
18
|
> A Terminal Kanban Application written in Python to boost your productivity :rocket:
|
|
@@ -35,7 +35,9 @@ It was a great help for developing my first package and I can highly recommend i
|
|
|
35
35
|
<details><summary>Colorful and Interactive</summary>
|
|
36
36
|
|
|
37
37
|
- kanban-python uses [rich] under the hood to process user input
|
|
38
|
-
and display nice looking
|
|
38
|
+
and display nice looking kanban-boards to the terminal.
|
|
39
|
+
- Each task has a unique `ID` per board and also has an optional `Tag` and `Due Date` associated with it,
|
|
40
|
+
which are displayed alongside its `Title`
|
|
39
41
|
|
|
40
42
|
</details>
|
|
41
43
|
|
|
@@ -77,7 +79,7 @@ This can be edited manually or within the kanban-python application. It tracks t
|
|
|
77
79
|
|
|
78
80
|
- Each created board comes with its own name and `pykanban.json` file,
|
|
79
81
|
which stores all tasks for that board. The files are stored in board specific folders under `$USER_DATA_DIR/kanban-python/kanban_boards/<BOARDNAME>`.
|
|
80
|
-
When changing Boards you also get an overview over tasks in visible columns for each board.
|
|
82
|
+
When changing Boards you also get an overview over tasks in visible columns for each board and the most urgent or overdue task on that board.
|
|
81
83
|

|
|
82
84
|
|
|
83
85
|
</details>
|
|
@@ -106,7 +108,7 @@ with `kanban configure`.
|
|
|
106
108
|
|
|
107
109
|
- When you use [kanban report](#create-report) a github-like contribution map is displayed for the current year,
|
|
108
110
|
Also a markdown file is created with all tasks comleted based on the moment, when the tasks were moved to Done Column.
|
|
109
|
-

|
|
110
112
|
|
|
111
113
|
</details>
|
|
112
114
|
|
|
@@ -156,7 +158,7 @@ The filepath were the task was found will be added as description of the task.
|
|
|
156
158
|
```
|
|
157
159
|
Goes over all your Boards and creates a single markdown file by checking the `Completion Dates` of your tasks.
|
|
158
160
|
Also shows a nice github-like contribution table for the current year.
|
|
159
|
-

|
|
160
162
|
|
|
161
163
|
### Change Settings
|
|
162
164
|
```bash
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -30,7 +30,7 @@ QUOTES = [
|
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
BOARD_CAPTION_STRING = "Tasks have the following Structure:\
|
|
33
|
-
[[cyan]ID[/]] ([orange3]TAG[/]) [white]Task Title[/]"
|
|
33
|
+
[[cyan]ID[/]] ([orange3]TAG[/]) [white]Task Title[/] |[red]Days Left[/]|"
|
|
34
34
|
|
|
35
35
|
COLOR_DICT = {
|
|
36
36
|
"Ready": "[red]Ready[/]",
|
|
@@ -49,6 +49,7 @@ DUMMY_TASK = {
|
|
|
49
49
|
"Complete_Time": "",
|
|
50
50
|
"Duration": 0,
|
|
51
51
|
"Creation_Date": "",
|
|
52
|
+
"Due_Date": "",
|
|
52
53
|
}
|
|
53
54
|
DUMMY_DB = {1: DUMMY_TASK}
|
|
54
55
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from json import dump, load
|
|
2
4
|
|
|
3
5
|
from rich.pretty import pprint
|
|
@@ -197,7 +199,7 @@ def show_tasks():
|
|
|
197
199
|
{
|
|
198
200
|
key: val
|
|
199
201
|
for key, val in task.items()
|
|
200
|
-
if key in ["Title", "Description", "Tag", "Status"]
|
|
202
|
+
if key in ["Title", "Description", "Tag", "Status", "Due_Date"]
|
|
201
203
|
},
|
|
202
204
|
console=console,
|
|
203
205
|
expand_all=True,
|
|
@@ -14,12 +14,16 @@ from .constants import (
|
|
|
14
14
|
REPORT_COLORS,
|
|
15
15
|
)
|
|
16
16
|
from .utils import (
|
|
17
|
+
calculate_days_left_till_due,
|
|
17
18
|
calculate_time_delta_str,
|
|
19
|
+
check_due_date_format,
|
|
18
20
|
console,
|
|
19
21
|
create_color_mapping,
|
|
20
22
|
create_dict_for_report_view,
|
|
21
23
|
create_status_dict_for_rows,
|
|
22
24
|
current_time_to_str,
|
|
25
|
+
due_date_date_to_datetime,
|
|
26
|
+
due_date_datetime_to_date,
|
|
23
27
|
)
|
|
24
28
|
|
|
25
29
|
|
|
@@ -95,26 +99,40 @@ def input_ask_for_action():
|
|
|
95
99
|
# Action 1: New Task
|
|
96
100
|
def input_create_new_task() -> dict:
|
|
97
101
|
title = Prompt.ask(
|
|
98
|
-
prompt="[1/
|
|
102
|
+
prompt="[1/5] Add Task Title",
|
|
99
103
|
)
|
|
100
104
|
|
|
101
105
|
description = Prompt.ask(
|
|
102
|
-
prompt="[2/
|
|
106
|
+
prompt="[2/5] Add Task Description",
|
|
103
107
|
show_default=True,
|
|
104
|
-
default=
|
|
108
|
+
default="",
|
|
105
109
|
)
|
|
106
110
|
|
|
107
111
|
tag = Prompt.ask(
|
|
108
|
-
prompt="[3/
|
|
112
|
+
prompt="[3/5] Add a Tag",
|
|
109
113
|
show_default=True,
|
|
110
114
|
default="ETC",
|
|
111
115
|
)
|
|
112
116
|
|
|
117
|
+
while True:
|
|
118
|
+
due_date = Prompt.ask(
|
|
119
|
+
prompt="[4/5] Add a Due Date (YYYY-MM-DD)",
|
|
120
|
+
show_default=True,
|
|
121
|
+
default="",
|
|
122
|
+
)
|
|
123
|
+
if not due_date or check_due_date_format(date_str=due_date):
|
|
124
|
+
break
|
|
125
|
+
else:
|
|
126
|
+
console.print(
|
|
127
|
+
f":warning: '{due_date}' has [red]not[/] "
|
|
128
|
+
+ "the right format YYYY-MM-DD"
|
|
129
|
+
)
|
|
130
|
+
|
|
113
131
|
console.print(f"\t[1] {COLOR_DICT['Ready']}")
|
|
114
132
|
console.print(f"\t[2] {COLOR_DICT['Doing']}")
|
|
115
133
|
|
|
116
134
|
status = IntPrompt.ask(
|
|
117
|
-
prompt="[
|
|
135
|
+
prompt="[5/5] Status of Task",
|
|
118
136
|
show_choices=False,
|
|
119
137
|
choices=["1", "2"],
|
|
120
138
|
show_default=True,
|
|
@@ -127,6 +145,7 @@ def input_create_new_task() -> dict:
|
|
|
127
145
|
"Status": "Ready" if str(status) == "1" else "Doing",
|
|
128
146
|
"Tag": tag.upper(),
|
|
129
147
|
"Creation_Date": current_time_to_str(),
|
|
148
|
+
"Due_Date": due_date_date_to_datetime(due_date),
|
|
130
149
|
"Begin_Time": current_time_to_str() if str(status) == "2" else "",
|
|
131
150
|
"Complete_Time": "",
|
|
132
151
|
"Duration": 0,
|
|
@@ -149,7 +168,7 @@ def input_ask_which_task_to_update(data: dict) -> str:
|
|
|
149
168
|
|
|
150
169
|
def input_update_task_title(current_title) -> str:
|
|
151
170
|
return Prompt.ask(
|
|
152
|
-
prompt="[1/
|
|
171
|
+
prompt="[1/5] Update Task Title",
|
|
153
172
|
show_default=True,
|
|
154
173
|
default=current_title,
|
|
155
174
|
)
|
|
@@ -157,7 +176,7 @@ def input_update_task_title(current_title) -> str:
|
|
|
157
176
|
|
|
158
177
|
def input_update_task_description(current_desc) -> str:
|
|
159
178
|
return Prompt.ask(
|
|
160
|
-
prompt="[2/
|
|
179
|
+
prompt="[2/5] Update Task Description",
|
|
161
180
|
show_default=True,
|
|
162
181
|
default=current_desc,
|
|
163
182
|
)
|
|
@@ -165,16 +184,52 @@ def input_update_task_description(current_desc) -> str:
|
|
|
165
184
|
|
|
166
185
|
def input_update_task_tag(current_tag) -> str:
|
|
167
186
|
return Prompt.ask(
|
|
168
|
-
prompt="[3/
|
|
187
|
+
prompt="[3/5] Update Tag",
|
|
169
188
|
show_default=True,
|
|
170
189
|
default=current_tag,
|
|
171
190
|
)
|
|
172
191
|
|
|
173
192
|
|
|
193
|
+
def input_update_due_date(current_due) -> str:
|
|
194
|
+
while True:
|
|
195
|
+
due_date_str = Prompt.ask(
|
|
196
|
+
prompt="[4/5] Update Due Date (YYYY-MM-DD or ` `)",
|
|
197
|
+
show_default=True,
|
|
198
|
+
# fix default view
|
|
199
|
+
default=due_date_datetime_to_date(date_datetime=current_due),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if not due_date_str or check_due_date_format(date_str=due_date_str):
|
|
203
|
+
break
|
|
204
|
+
else:
|
|
205
|
+
console.print(
|
|
206
|
+
f":warning: '{due_date_str}' has [red]not[/] "
|
|
207
|
+
+ "the right format YYYY-MM-DD"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return due_date_date_to_datetime(due_date_str)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def input_ask_to_what_status_to_move(task_title):
|
|
214
|
+
possible_status = [cat for cat in cfg.kanban_columns_dict]
|
|
215
|
+
|
|
216
|
+
console.print(f'Updating Status of Task "[white]{task_title}[/]"')
|
|
217
|
+
for idx, status in enumerate(possible_status, start=1):
|
|
218
|
+
console.print(f"\t[{idx}] {COLOR_DICT.get(status, status)}")
|
|
219
|
+
|
|
220
|
+
new_status = IntPrompt.ask(
|
|
221
|
+
prompt="[5/5] New Status of Task?",
|
|
222
|
+
show_choices=False,
|
|
223
|
+
choices=[f"{i}" for i, _ in enumerate(possible_status, start=1)],
|
|
224
|
+
)
|
|
225
|
+
return possible_status[int(new_status) - 1]
|
|
226
|
+
|
|
227
|
+
|
|
174
228
|
def input_update_task(current_task: dict) -> dict:
|
|
175
229
|
title = input_update_task_title(current_task["Title"])
|
|
176
230
|
description = input_update_task_description(current_task["Description"])
|
|
177
231
|
tag = input_update_task_tag(current_task["Tag"])
|
|
232
|
+
due_date = input_update_due_date(current_task.get("Due_Date", ""))
|
|
178
233
|
status = input_ask_to_what_status_to_move(current_task["Title"])
|
|
179
234
|
|
|
180
235
|
if (status == "Doing") and (current_task["Status"] != "Doing"):
|
|
@@ -204,6 +259,7 @@ def input_update_task(current_task: dict) -> dict:
|
|
|
204
259
|
"Description": description,
|
|
205
260
|
"Status": status,
|
|
206
261
|
"Tag": tag.upper(),
|
|
262
|
+
"Due_Date": due_date,
|
|
207
263
|
"Begin_Time": start_doing,
|
|
208
264
|
"Complete_Time": stop_doing,
|
|
209
265
|
"Duration": duration,
|
|
@@ -212,21 +268,6 @@ def input_update_task(current_task: dict) -> dict:
|
|
|
212
268
|
return current_task
|
|
213
269
|
|
|
214
270
|
|
|
215
|
-
def input_ask_to_what_status_to_move(task_title):
|
|
216
|
-
possible_status = [cat for cat in cfg.kanban_columns_dict]
|
|
217
|
-
|
|
218
|
-
console.print(f'Updating Status of Task "[white]{task_title}[/]"')
|
|
219
|
-
for idx, status in enumerate(possible_status, start=1):
|
|
220
|
-
console.print(f"\t[{idx}] {COLOR_DICT.get(status, status)}")
|
|
221
|
-
|
|
222
|
-
new_status = IntPrompt.ask(
|
|
223
|
-
prompt="[4/4] New Status of Task?",
|
|
224
|
-
show_choices=False,
|
|
225
|
-
choices=[f"{i}" for i, _ in enumerate(possible_status, start=1)],
|
|
226
|
-
)
|
|
227
|
-
return possible_status[int(new_status) - 1]
|
|
228
|
-
|
|
229
|
-
|
|
230
271
|
def input_confirm_set_board_active(name) -> bool:
|
|
231
272
|
return Confirm.ask(
|
|
232
273
|
f"Do you want to set the Board '{name}' as active:question_mark:"
|
|
@@ -255,6 +296,13 @@ def input_ask_for_change_board(boards_dict: dict) -> str:
|
|
|
255
296
|
|
|
256
297
|
for idx, (board, board_data) in enumerate(boards_dict.items(), start=1):
|
|
257
298
|
status_dict = create_status_dict_for_rows(board_data, cfg.vis_cols)
|
|
299
|
+
days_left_list = [
|
|
300
|
+
calculate_days_left_till_due(val["Due_Date"])
|
|
301
|
+
for val in board_data.values()
|
|
302
|
+
if (val.get("Due_Date") and (val["Status"] in ["Ready", "Doing"]))
|
|
303
|
+
]
|
|
304
|
+
# Use -9999 to as placeholder for no tasks to make comparison later
|
|
305
|
+
days_left = min(days_left_list) if days_left_list else -9999
|
|
258
306
|
console.print(
|
|
259
307
|
f"[{idx}] {board}"
|
|
260
308
|
+ " " * ((max_board_len - len(board) + 1))
|
|
@@ -264,6 +312,13 @@ def input_ask_for_change_board(boards_dict: dict) -> str:
|
|
|
264
312
|
for col in cfg.vis_cols
|
|
265
313
|
]
|
|
266
314
|
)
|
|
315
|
+
+ (
|
|
316
|
+
f"\t next due in {days_left} day/s"
|
|
317
|
+
if days_left > 0
|
|
318
|
+
else f"[red]\t task {-days_left} day/s overdue[/]"
|
|
319
|
+
if days_left != -9999
|
|
320
|
+
else "\t no dues present here"
|
|
321
|
+
)
|
|
267
322
|
)
|
|
268
323
|
|
|
269
324
|
answer = IntPrompt.ask(
|
|
@@ -328,7 +383,7 @@ def print_all_todos(todos: list) -> None:
|
|
|
328
383
|
console.print(todo_string)
|
|
329
384
|
|
|
330
385
|
|
|
331
|
-
def input_confirm_add_todos_to_board(todos) -> bool:
|
|
386
|
+
def input_confirm_add_todos_to_board(todos: list) -> bool:
|
|
332
387
|
# Question Also print tasks already in Board?
|
|
333
388
|
console.print(f"Found [blue]{len(todos)}[/] TODOs.")
|
|
334
389
|
if len(todos) > 10:
|
|
@@ -393,7 +448,7 @@ def create_github_like_report_table(boards_dict: dict):
|
|
|
393
448
|
|
|
394
449
|
|
|
395
450
|
# Ask for Actions
|
|
396
|
-
def input_ask_for_action_settings():
|
|
451
|
+
def input_ask_for_action_settings() -> int:
|
|
397
452
|
console.print(
|
|
398
453
|
"[yellow]Not happy with current settings!?[/],"
|
|
399
454
|
+ "which [blue]Section[/] do you want to change :hammer_and_wrench:?"
|
|
@@ -458,7 +513,7 @@ def input_change_footer_settings():
|
|
|
458
513
|
return footer_visible
|
|
459
514
|
|
|
460
515
|
|
|
461
|
-
def input_change_done_limit_settings():
|
|
516
|
+
def input_change_done_limit_settings() -> int:
|
|
462
517
|
done_limit = IntPrompt.ask(
|
|
463
518
|
prompt=f"What should the Limit of Tasks in {COLOR_DICT.get('Done','Done')} "
|
|
464
519
|
+ f"Column be, before moving to {COLOR_DICT.get('Archived','Archived')}?",
|
|
@@ -41,6 +41,10 @@ def create_status_dict_for_rows(data: dict, vis_cols: list) -> dict:
|
|
|
41
41
|
task_str = f"[[cyan]{id}[/]]" if int(id) > 9 else f"[[cyan]0{id}[/]]"
|
|
42
42
|
task_str += f'([orange3]{task.get("Tag")}[/])'
|
|
43
43
|
task_str += f' [white]{task["Title"]}[/]'
|
|
44
|
+
# Add days left
|
|
45
|
+
if all((task["Status"] in ["Ready", "Doing"], task.get("Due_Date", False))):
|
|
46
|
+
days_left = calculate_days_left_till_due(task["Due_Date"])
|
|
47
|
+
task_str += f" |[red]{days_left:02d}[/]|"
|
|
44
48
|
status_dict[task["Status"]].append(task_str)
|
|
45
49
|
|
|
46
50
|
return status_dict
|
|
@@ -198,6 +202,8 @@ def create_color_mapping(amount_list: list, max_val: int):
|
|
|
198
202
|
mapped_list.append(3)
|
|
199
203
|
elif (val / max_val) <= 1:
|
|
200
204
|
mapped_list.append(4)
|
|
205
|
+
else:
|
|
206
|
+
continue
|
|
201
207
|
|
|
202
208
|
return mapped_list
|
|
203
209
|
|
|
@@ -233,3 +239,35 @@ def create_report_document(boards_dict: dict):
|
|
|
233
239
|
report_file.write("".join(completed))
|
|
234
240
|
|
|
235
241
|
return date_dict
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def check_due_date_format(date_str: str) -> bool:
|
|
245
|
+
try:
|
|
246
|
+
datetime.strptime(date_str, "%Y-%m-%d")
|
|
247
|
+
return True
|
|
248
|
+
except ValueError:
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def due_date_datetime_to_date(date_datetime: str) -> str:
|
|
253
|
+
if date_datetime:
|
|
254
|
+
date_str = str(datetime.strptime(date_datetime, "%Y-%m-%d %H:%M:%S").date())
|
|
255
|
+
return date_str
|
|
256
|
+
return date_datetime
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def due_date_date_to_datetime(date_str: str) -> str:
|
|
260
|
+
if date_str:
|
|
261
|
+
date_datetime = str(
|
|
262
|
+
datetime.strptime(f"{date_str} 23:59:59", "%Y-%m-%d %H:%M:%S")
|
|
263
|
+
)
|
|
264
|
+
return date_datetime
|
|
265
|
+
return date_str
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def calculate_days_left_till_due(due_date: str):
|
|
269
|
+
time_now = datetime.now()
|
|
270
|
+
time_due = datetime.strptime(due_date, "%Y-%m-%d %H:%M:%S")
|
|
271
|
+
|
|
272
|
+
delta_days = (time_due - time_now).days
|
|
273
|
+
return delta_days
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kanban-python
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.9
|
|
4
4
|
Summary: Terminal Kanban App written in Python
|
|
5
5
|
Home-page: https://github.com/Zaloog/kanban-python
|
|
6
6
|
Author: Zaloog
|
|
@@ -19,6 +19,7 @@ Requires-Dist: platformdirs<4,>=3
|
|
|
19
19
|
Provides-Extra: testing
|
|
20
20
|
Requires-Dist: setuptools; extra == "testing"
|
|
21
21
|
Requires-Dist: pytest; extra == "testing"
|
|
22
|
+
Requires-Dist: freezegun; extra == "testing"
|
|
22
23
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
23
24
|
|
|
24
25
|
<!-- These are examples of badges you might want to add to your README:
|
|
@@ -35,7 +36,7 @@ Requires-Dist: pytest-cov; extra == "testing"
|
|
|
35
36
|
[](https://pyscaffold.org/)
|
|
36
37
|
[](https://pypi.org/project/kanban-python/)
|
|
37
38
|
[](https://pepy.tech/project/kanban-python)
|
|
38
|
-
[](https://coveralls.io/github/Zaloog/kanban-python?branch=refs/tags/v0.3.9)
|
|
39
40
|
# kanban-python
|
|
40
41
|
|
|
41
42
|
> A Terminal Kanban Application written in Python to boost your productivity :rocket:
|
|
@@ -58,7 +59,9 @@ It was a great help for developing my first package and I can highly recommend i
|
|
|
58
59
|
<details><summary>Colorful and Interactive</summary>
|
|
59
60
|
|
|
60
61
|
- kanban-python uses [rich] under the hood to process user input
|
|
61
|
-
and display nice looking
|
|
62
|
+
and display nice looking kanban-boards to the terminal.
|
|
63
|
+
- Each task has a unique `ID` per board and also has an optional `Tag` and `Due Date` associated with it,
|
|
64
|
+
which are displayed alongside its `Title`
|
|
62
65
|
|
|
63
66
|
</details>
|
|
64
67
|
|
|
@@ -100,7 +103,7 @@ This can be edited manually or within the kanban-python application. It tracks t
|
|
|
100
103
|
|
|
101
104
|
- Each created board comes with its own name and `pykanban.json` file,
|
|
102
105
|
which stores all tasks for that board. The files are stored in board specific folders under `$USER_DATA_DIR/kanban-python/kanban_boards/<BOARDNAME>`.
|
|
103
|
-
When changing Boards you also get an overview over tasks in visible columns for each board.
|
|
106
|
+
When changing Boards you also get an overview over tasks in visible columns for each board and the most urgent or overdue task on that board.
|
|
104
107
|

|
|
105
108
|
|
|
106
109
|
</details>
|
|
@@ -129,7 +132,7 @@ with `kanban configure`.
|
|
|
129
132
|
|
|
130
133
|
- When you use [kanban report](#create-report) a github-like contribution map is displayed for the current year,
|
|
131
134
|
Also a markdown file is created with all tasks comleted based on the moment, when the tasks were moved to Done Column.
|
|
132
|
-

|
|
133
136
|
|
|
134
137
|
</details>
|
|
135
138
|
|
|
@@ -179,7 +182,7 @@ The filepath were the task was found will be added as description of the task.
|
|
|
179
182
|
```
|
|
180
183
|
Goes over all your Boards and creates a single markdown file by checking the `Completion Dates` of your tasks.
|
|
181
184
|
Also shows a nice github-like contribution table for the current year.
|
|
182
|
-

|
|
183
186
|
|
|
184
187
|
### Change Settings
|
|
185
188
|
```bash
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import datetime
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from freezegun import freeze_time
|
|
4
5
|
|
|
5
6
|
from kanban_python import utils
|
|
6
7
|
|
|
@@ -10,7 +11,7 @@ def test_get_motivational_quote():
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def test_current_time_to_str():
|
|
13
|
-
current_time = datetime.now()
|
|
14
|
+
current_time = datetime.datetime.now()
|
|
14
15
|
result = utils.current_time_to_str()
|
|
15
16
|
expected_format = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
16
17
|
|
|
@@ -198,14 +199,80 @@ def test_get_iso_calender_info():
|
|
|
198
199
|
assert weekday == 2
|
|
199
200
|
|
|
200
201
|
|
|
202
|
+
def test_create_dict_for_report_view():
|
|
203
|
+
tasks = [
|
|
204
|
+
{"Complete_Time": "2022-12-05 23:41:41"},
|
|
205
|
+
{"Complete_Time": "2023-12-05 23:41:41"},
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
result_max = 1
|
|
209
|
+
result_dict = {2: {49: 1}}
|
|
210
|
+
|
|
211
|
+
max_val, report_dict = utils.create_dict_for_report_view(tasks)
|
|
212
|
+
assert result_max == max_val
|
|
213
|
+
assert result_dict == report_dict
|
|
214
|
+
|
|
215
|
+
|
|
201
216
|
def test_create_color_mapping():
|
|
202
|
-
task_amount = [1, 3, 4, 0, 5, 8, 12, 16]
|
|
203
|
-
max_val =
|
|
217
|
+
task_amount = [1, 3, 4, 0, 5, 8, 12, 16, 25]
|
|
218
|
+
max_val = 16
|
|
204
219
|
result = utils.create_color_mapping(amount_list=task_amount, max_val=max_val)
|
|
205
220
|
|
|
206
221
|
assert result == [1, 1, 1, 0, 2, 2, 3, 4]
|
|
207
222
|
|
|
208
223
|
|
|
224
|
+
@pytest.mark.parametrize(
|
|
225
|
+
"date, expected_result",
|
|
226
|
+
[
|
|
227
|
+
("2023-12-24", True),
|
|
228
|
+
("2023-17-3", False),
|
|
229
|
+
("30.05.2023", False),
|
|
230
|
+
("30.05.223", False),
|
|
231
|
+
],
|
|
232
|
+
)
|
|
233
|
+
def test_check_due_date_format(date, expected_result):
|
|
234
|
+
result = utils.check_due_date_format(date)
|
|
235
|
+
|
|
236
|
+
assert result is expected_result
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@pytest.mark.parametrize(
|
|
240
|
+
"datetime, expected_result",
|
|
241
|
+
[
|
|
242
|
+
("2023-12-24 00:00:00", "2023-12-24"),
|
|
243
|
+
("", ""),
|
|
244
|
+
],
|
|
245
|
+
)
|
|
246
|
+
def test_due_date_datetime_to_date(datetime, expected_result):
|
|
247
|
+
result = utils.due_date_datetime_to_date(datetime)
|
|
248
|
+
|
|
249
|
+
assert result == expected_result
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@pytest.mark.parametrize(
|
|
253
|
+
"datetime, expected_result",
|
|
254
|
+
[
|
|
255
|
+
("2023-12-24", "2023-12-24 23:59:59"),
|
|
256
|
+
("", ""),
|
|
257
|
+
],
|
|
258
|
+
)
|
|
259
|
+
def test_due_date_date_to_datetime(datetime, expected_result):
|
|
260
|
+
result = utils.due_date_date_to_datetime(datetime)
|
|
261
|
+
|
|
262
|
+
assert result == expected_result
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_calculate_days_left_till_due():
|
|
266
|
+
fake_now = datetime.datetime(2023, 12, 10, 0, 0, 0)
|
|
267
|
+
test_time = "2023-12-24 23:59:59"
|
|
268
|
+
delta_days = 14
|
|
269
|
+
|
|
270
|
+
with freeze_time(fake_now):
|
|
271
|
+
result = utils.calculate_days_left_till_due(test_time)
|
|
272
|
+
|
|
273
|
+
assert result == delta_days
|
|
274
|
+
|
|
275
|
+
|
|
209
276
|
# def test_main(capsys):
|
|
210
277
|
# """CLI Tests"""
|
|
211
278
|
# # capsys is a pytest fixture that allows asserts against stdout/stderr
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|