Skip to content

Commit 30be856

Browse files
authored
Merge pull request #256 from wasi0013/v0.0.14
V0.0.14
2 parents 98c3dd4 + 3515d5a commit 30be856

9 files changed

Lines changed: 366 additions & 204 deletions

File tree

HISTORY.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,13 @@ History
8686
0.0.13 (2023-11-07)
8787
-------------------
8888

89-
* Made the init command optional.
89+
* Made the init command optional.
90+
91+
0.0.14 (2023-11-07)
92+
-------------------
93+
94+
* config invoice command bug fix.
95+
* copy logo instead of moving it permanently when configuring invoice.
96+
* fixed f strings quote issue.
97+
* made init command hidden.
98+
* improved doc strings and help texts.

PyTM/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__author__ = "Wasi"
22
__email__ = "wasi0013@gmail.com"
3-
__version__ = "0.0.13"
3+
__version__ = "0.0.14"

PyTM/cli.py

Lines changed: 243 additions & 134 deletions
Large diffs are not rendered by default.

PyTM/commands/project.py

Lines changed: 77 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,132 @@
1-
import click
2-
from PyTM.core.project_handler import create_project, pause_project, finish_project, project_summary, project_status, remove_project, abort_project
31
from functools import partial
4-
from PyTM.core.data_handler import update, load_data, save_data
5-
import PyTM.core.task_handler as task_handler
6-
import PyTM.settings as settings
7-
from PyTM.console import console
8-
from rich.tree import Tree
2+
3+
import click
94
from rich.panel import Panel
10-
import json
5+
from rich.tree import Tree
6+
7+
from PyTM import settings
8+
from PyTM.console import console
9+
from PyTM.core import task_handler
10+
from PyTM.core import data_handler
11+
from PyTM.core import project_handler
1112

1213

1314
def _get_duration_str(sum_of_durations):
1415
m, s = divmod(sum_of_durations, 60)
15-
duration = ''
16+
duration = ""
1617
if m > 60:
1718
h, m = divmod(m, 60)
1819
if h > 24:
1920
d, h = divmod(h, 24)
20-
duration = f'{d:d} days {h:d} hours {m:02d} mins {s:02d} secs'
21+
duration = f"{d:d} days {h:d} hours {m:02d} mins {s:02d} secs"
2122
else:
22-
duration = f'{h:d} hours {m:02d} mins {s:02d} secs'
23+
duration = f"{h:d} hours {m:02d} mins {s:02d} secs"
2324
elif m < 1:
24-
duration = f'{s:02d} seconds'
25+
duration = f"{s:02d} seconds"
2526
else:
26-
duration = f'{m:02d} mins {s:02d} secs'
27+
duration = f"{m:02d} mins {s:02d} secs"
2728
return duration
2829

30+
2931
@click.group()
3032
def project():
3133
"""
3234
pytm sub-command for managing projects.
3335
"""
3436
pass
3537

38+
3639
@project.command()
3740
def abort():
3841
"""
3942
- aborts the current project and task.
4043
"""
41-
state = load_data(settings.state_filepath)
44+
state = data_handler.load_data(settings.state_filepath)
4245
project_name = state.get(settings.CURRENT_PROJECT)
4346
if project_name:
44-
update(partial(abort_project, project_name=project_name))
47+
data_handler.update(partial(project_handler.abort, project_name=project_name))
4548
state[settings.CURRENT_PROJECT] = ""
4649
if state[settings.CURRENT_TASK]:
47-
update(partial(task_handler.abort, project_name=project_name, task_name=state[settings.CURRENT_TASK]))
50+
data_handler.update(
51+
partial(
52+
task_handler.abort,
53+
project_name=project_name,
54+
task_name=state[settings.CURRENT_TASK],
55+
)
56+
)
4857
state[settings.CURRENT_TASK] = ""
49-
save_data(state, settings.state_filepath)
58+
data_handler.save_data(state, settings.state_filepath)
5059
console.print(f"[bold blue]{project_name}[/bold blue] aborted.")
5160
else:
5261
console.print("[bold red]No active project.")
5362

63+
5464
@project.command()
5565
def finish():
5666
"""
5767
- marks the current project as finished.
5868
"""
59-
state = load_data(settings.state_filepath)
69+
state = data_handler.load_data(settings.state_filepath)
6070
project_name = state.get(settings.CURRENT_PROJECT)
6171
if project_name:
62-
update(partial(finish_project, project_name=project_name))
72+
data_handler.update(partial(project_handler.finish, project_name=project_name))
6373
state[settings.CURRENT_PROJECT] = ""
6474
if state[settings.CURRENT_TASK]:
65-
update(partial(task_handler.finish, project_name=project_name, task_name=state[settings.CURRENT_TASK]))
75+
data_handler.update(
76+
partial(
77+
task_handler.finish,
78+
project_name=project_name,
79+
task_name=state[settings.CURRENT_TASK],
80+
)
81+
)
6682
state[settings.CURRENT_TASK] = ""
67-
save_data(state, settings.state_filepath)
83+
data_handler.save_data(state, settings.state_filepath)
6884
console.print(f"[bold blue]{project_name}[/bold blue] finished.")
6985
else:
7086
console.print("[bold red]No active project.")
7187

88+
7289
@project.command()
7390
def pause():
7491
"""
75-
- pauses the current project, so new tasks can't be started.
92+
- pauses the current project, so new tasks can't be started.
7693
"""
77-
state = load_data(settings.state_filepath)
94+
state = data_handler.load_data(settings.state_filepath)
7895
project_name = state.get(settings.CURRENT_PROJECT)
7996
if project_name:
80-
update(partial(pause_project, project_name=project_name))
97+
data_handler.update(partial(project_handler.pause, project_name=project_name))
8198
state[settings.CURRENT_PROJECT] = ""
8299
if state[settings.CURRENT_TASK]:
83-
update(partial(task_handler.pause, project_name=project_name, task_name=state[settings.CURRENT_TASK]))
100+
data_handler.update(
101+
partial(
102+
task_handler.pause,
103+
project_name=project_name,
104+
task_name=state[settings.CURRENT_TASK],
105+
)
106+
)
84107
state[settings.CURRENT_TASK] = ""
85-
save_data(state, settings.state_filepath)
108+
data_handler.save_data(state, settings.state_filepath)
86109
console.print(f"[bold blue]{project_name}[/bold blue] paused.")
87110
else:
88111
console.print("[bold red]No active project.")
89112

113+
90114
@project.command()
91115
@click.argument("project_name")
92116
def start(project_name):
93117
"""
94118
- starts an existing project or creates a new project.
95119
"""
96-
data = load_data()
97-
update(partial(create_project, project_name=project_name))
98-
state = load_data(settings.state_filepath)
120+
data = data_handler.load_data()
121+
data_handler.update(partial(project_handler.create, project_name=project_name))
122+
state = data_handler.load_data(settings.state_filepath)
99123
state[settings.CURRENT_PROJECT] = project_name
100-
save_data(state, settings.state_filepath)
124+
data_handler.save_data(state, settings.state_filepath)
101125
console.print(f"[bold blue]{project_name}[/bold blue] started.")
102126
if project_name not in data.keys():
103-
console.print(f"\n[bold blue i on white]You also might want to run: `pytm config project {project_name}` to configure project meta data.[/bold blue i on white]")
127+
console.print(
128+
f"\n[bold blue i on white]You also might want to run: `pytm config project {project_name}` to configure project meta data.[/bold blue i on white]"
129+
)
104130

105131

106132
@project.command()
@@ -109,42 +135,51 @@ def remove(project_name):
109135
"""
110136
- deletes a project and related tasks.
111137
"""
112-
state = load_data(settings.state_filepath)
113-
update(partial(remove_project, project_name=project_name))
138+
state = data_handler.load_data(settings.state_filepath)
139+
data_handler.update(partial(project_handler.remove, project_name=project_name))
114140
if state[settings.CURRENT_PROJECT] == project_name:
115141
state[settings.CURRENT_PROJECT] = ""
116-
save_data(state, settings.state_filepath)
142+
data_handler.save_data(state, settings.state_filepath)
117143
console.print(f"[bold blue]{project_name}[/bold blue] removed.")
118144

145+
119146
@project.command()
120147
@click.argument("project_name")
121148
def status(project_name):
122149
"""
123-
- of the project (running, paused, finished, etc).
150+
- of the project (running, paused, finished, etc).
124151
"""
125-
console.print(f"[bold blue]{project_name}[/bold blue] status: {project_status(load_data(), project_name)}")
152+
console.print(
153+
f"[bold blue]{project_name}[/bold blue] status: {project_handler.status(data_handler.load_data(), project_name)}"
154+
)
155+
126156

127157
@project.command()
128158
@click.argument("project_name")
129159
def summary(project_name):
130160
"""
131161
- shows total time of the project with task and duration.
132162
"""
133-
project = project_summary(load_data(), project_name)
134-
project_data = project['tasks']
135-
tree = Tree(f"[bold blue]{project_name}[/bold blue] ([i]{project["status"]}[/i])")
163+
project = project_handler.summary(data_handler.load_data(), project_name)
164+
project_data = project["tasks"]
165+
tree = Tree(f'[bold blue]{project_name}[/bold blue] ([i]{project["status"]}[/i])')
136166
duration = 0
137167
for task, t in project_data.items():
138-
task_duration = int(round(t['duration']))
168+
task_duration = int(round(t["duration"]))
139169
duration += task_duration
140-
tree.add(f"[green]{task}[/green]: {_get_duration_str(task_duration)} ([i]{t['status']}[/i])")
170+
tree.add(
171+
f"[green]{task}[/green]: {_get_duration_str(task_duration)} ([i]{t['status']}[/i])"
172+
)
141173
console.print(Panel.fit(tree))
142174
console.print(f"[blue bold]Total time[/blue bold]: {_get_duration_str(duration)}")
143175

176+
144177
@project.command()
145178
@click.argument("project_name")
146179
def json(project_name):
147180
"""
148181
- shows project data in json format.
149182
"""
150-
console.print_json(data=project_summary(load_data(), project_name))
183+
console.print_json(
184+
data=project_handler.summary(data_handler.load_data(), project_name)
185+
)

PyTM/commands/task.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import click
22
from functools import partial
3-
import PyTM.core.task_handler as task_handler
4-
import PyTM.core.data_handler as data_handler
5-
import PyTM.settings as settings
3+
from PyTM.core import task_handler
4+
from PyTM.core import data_handler
5+
from PyTM import settings
66
from PyTM.console import console
77
import json
88

PyTM/core/invoice_handler.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
import PyTM.settings as settings
2-
import PyTM.core.data_handler as data_handler
1+
from PyTM import settings
2+
from PyTM.core import data_handler
33
from PyTM.commands.project import _get_duration_str
44
import datetime
55

6+
67
def generate(invoice_number, invoice_texts, user, project, discount=0):
7-
title, logo, foot_note = invoice_texts['title'], invoice_texts['logo'], invoice_texts['foot_note']
8-
tasks = project['tasks']
8+
title, logo, foot_note = (
9+
invoice_texts["title"],
10+
invoice_texts["logo"],
11+
invoice_texts["foot_note"],
12+
)
13+
tasks = project["tasks"]
914
duration = 0
1015
sub_total = 0
11-
16+
1217
for task, t in tasks.items():
13-
if t['status'] != settings.ABORTED:
14-
task_duration = int(round(t['duration']))
18+
if t["status"] != settings.ABORTED:
19+
task_duration = int(round(t["duration"]))
1520
duration += task_duration
16-
sub_total += float(t['duration']/360) * float(user['hourly_rate'])
17-
if discount == "": discount = 0
18-
else: discount = float(discount)
21+
sub_total += float(t["duration"] / 360) * float(user["hourly_rate"])
22+
if discount == "":
23+
discount = 0
24+
else:
25+
discount = float(discount)
1926
total = sub_total - discount
2027
return f"""<!DOCTYPE html>
2128
<html lang="en">
@@ -68,13 +75,13 @@ def generate(invoice_number, invoice_texts, user, project, discount=0):
6875
</thead>
6976
<tbody>
7077
71-
{"\n".join(f"""<tr>
78+
{"\n".join(f'''<tr>
7279
<td class="p-2 border border-gray-300">{task.replace("_", " ").replace("-", " ").title()}</td>
7380
<td class="p-2 border border-gray-300">{t.get('description', '-')}</td>
7481
<td class="p-2 border border-gray-300">{float(t['duration']/360):,.02f}</td>
7582
<td class="p-2 border border-gray-300">{float(user['hourly_rate']):,.02f}$</td>
7683
<td class="p-2 border border-gray-300">{float(t['duration']/360) * float(user['hourly_rate']):,.02f}</td>
77-
</tr>""" for task, t in tasks.items())}
84+
</tr>''' for task, t in tasks.items())}
7885
7986
</tbody>
8087
</table>
@@ -106,4 +113,4 @@ def generate(invoice_number, invoice_texts, user, project, discount=0):
106113
</div>
107114
</body>
108115
</html>
109-
"""
116+
"""

PyTM/core/project_handler.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from datetime import datetime
2-
import PyTM.settings as settings
32

3+
from PyTM import settings
44

5-
def create_project(data, project_name):
5+
6+
def create(data, project_name):
67
"""Create & Start the project"""
78
if data.get(project_name):
89
data[project_name]["status"] = settings.STARTED
@@ -15,38 +16,38 @@ def create_project(data, project_name):
1516
return data
1617

1718

18-
def pause_project(data, project_name):
19+
def pause(data, project_name):
1920
"""Pause the project"""
2021
if data.get(project_name):
2122
data[project_name]["status"] = settings.PAUSED
2223
return data
2324

2425

25-
def finish_project(data, project_name):
26+
def finish(data, project_name):
2627
"""Finish the project"""
2728
if data.get(project_name):
2829
data[project_name]["status"] = settings.FINISHED
2930
return data
3031

3132

32-
def project_summary(data, project_name):
33+
def summary(data, project_name):
3334
"""Summarize the project"""
3435
return data.get(project_name, {})
3536

3637

37-
def project_status(data, project_name):
38+
def status(data, project_name):
3839
"""Status of the project"""
3940
return data.get(project_name, {}).get("status", "")
4041

4142

42-
def abort_project(data, project_name):
43+
def abort(data, project_name):
4344
"""Abort the project"""
4445
if data.get(project_name):
4546
data.get(project_name)["status"] = settings.ABORTED
4647
return data
4748

4849

49-
def remove_project(data, project_name):
50+
def remove(data, project_name):
5051
"""Remove the project"""
5152
if data.get(project_name):
5253
del data[project_name]

PyTM/core/task_handler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from datetime import datetime
2-
import PyTM.settings as settings
2+
3+
from PyTM import settings
34

45

56
def _calc_duration(date1, date2):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
setup(
2525
name="python-pytm",
26-
version="0.0.13",
26+
version="0.0.14",
2727
description="PyTM - an Open Source Python Time Management Tool for Mankind",
2828
long_description=readme + "\n\n" + doclink + "\n\n" + history,
2929
long_description_content_type="text/x-rst",

0 commit comments

Comments
 (0)