diff --git a/cli/trakcli/callbacks.py b/cli/trakcli/callbacks.py index 13926d4..1e660b3 100644 --- a/cli/trakcli/callbacks.py +++ b/cli/trakcli/callbacks.py @@ -11,8 +11,9 @@ def version_callback(value: bool) -> None: Print the application version. """ if value: + rprint("") rprint( - Panel( + Panel.fit( renderable=Align.center(f"{__app_name__} v{__version__}"), title=__app_name__, padding=(2), diff --git a/cli/trakcli/create/commands/project.py b/cli/trakcli/create/commands/project.py index cbd619d..3938d13 100644 --- a/cli/trakcli/create/commands/project.py +++ b/cli/trakcli/create/commands/project.py @@ -39,6 +39,7 @@ def create_project( tags = typer.prompt("Tags (CSV format)", default="") customer = typer.prompt("Customer", default="") hour_rate = typer.prompt("Hour rate", default=1, show_default=True) + archived = typer.prompt("Archived", default=False, show_default=True) if project_id: new_project = Project( @@ -51,6 +52,7 @@ def create_project( tags=[t.strip() for t in tags.split(",")] if tags != "" else [], customer=customer, rate=hour_rate, + archived=archived, ) with open(details_path, "w") as details_file: diff --git a/cli/trakcli/create/commands/work.py b/cli/trakcli/create/commands/work.py index 1cb2658..ebbb61c 100644 --- a/cli/trakcli/create/commands/work.py +++ b/cli/trakcli/create/commands/work.py @@ -5,7 +5,7 @@ from rich import print as rprint from rich.panel import Panel -from trakcli.projects.database import get_project_from_config, get_projects_from_config +from trakcli.projects.database import db_get_project_details, get_projects_from_config from trakcli.projects.utils.print_missing_project import print_missing_project from trakcli.utils.print_with_padding import print_with_padding from trakcli.works.database import ( @@ -88,7 +88,7 @@ def create_work( projects_in_config = get_projects_from_config(archived) if project_id in projects_in_config: - details = get_project_from_config(project_id) + details = db_get_project_details(project_id) # Check if project esists if details: diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index 454d623..1b74874 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -14,7 +14,7 @@ from trakcli.create import app as create_app from trakcli.dev.commands import app as dev_app from trakcli.initialize import initialize_trak -from trakcli.projects.commands import app as projects_app +from trakcli.projects import app as projects_app from trakcli.report import app as report_app from trakcli.tracker.commands.get_current_session_status import ( get_current_session_status, diff --git a/cli/trakcli/projects/__init__.py b/cli/trakcli/projects/__init__.py index e69de29..7704475 100644 --- a/cli/trakcli/projects/__init__.py +++ b/cli/trakcli/projects/__init__.py @@ -0,0 +1,12 @@ +import typer + +from trakcli.projects.commands.archive import command_project_archive +from trakcli.projects.commands.delete import command_project_delete +from trakcli.projects.commands.list import command_project_list + +app = typer.Typer() + + +app.command(name="list", help="List your projects.")(command_project_list) +app.command(name="delete", help="Delete a project.")(command_project_delete) +app.command(name="archive", help="Archive a project.")(command_project_archive) diff --git a/cli/trakcli/projects/commands/__init__.py b/cli/trakcli/projects/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/trakcli/projects/commands/archive.py b/cli/trakcli/projects/commands/archive.py new file mode 100644 index 0000000..30ff697 --- /dev/null +++ b/cli/trakcli/projects/commands/archive.py @@ -0,0 +1,70 @@ +import json +from typing import Annotated, Optional + +import questionary +import typer + +from trakcli.config.models import Project +from trakcli.projects.database import ( + db_get_project_details, + db_get_project_details_path, + get_projects_from_config, +) +from trakcli.projects.messages.print_project_archived_toggle import ( + print_project_archived_toggle, +) +from trakcli.projects.messages.print_project_broken_configuration import ( + print_project_broken_configuration, +) +from trakcli.projects.utils.print_missing_project import print_missing_project +from trakcli.projects.utils.print_no_projects import print_no_projects +from trakcli.utils.styles_questionary import questionary_style_select +from rich import print as rprint + + +def command_project_archive(project: Annotated[Optional[str], typer.Argument()] = None): + """Archive a project.""" + + projects_in_config = get_projects_from_config(True) + + # Check if there are configured projects + if not len(projects_in_config): + print_no_projects() + return + + # Provide the list of prjects to the user + if not project: + project = questionary.select( + "Select a project:", + choices=projects_in_config, + pointer="• ", + show_selected=True, + style=questionary_style_select, + ).ask() + + if not project: + return + + # Check if the project exists + if not project or project not in projects_in_config: + print_missing_project(projects_in_config) + return + + details_path = db_get_project_details_path(project) + details = db_get_project_details(project) + + if details_path and details: + # Toggle the value of archived + details = details._replace(archived=not details.archived) + + with open(details_path, "w") as details_file: + json.dump( + details._asdict(), + details_file, + indent=2, + separators=(",", ": "), + ) + print_project_archived_toggle(project, details.archived) + else: + print_project_broken_configuration(project) + return diff --git a/cli/trakcli/projects/commands.py b/cli/trakcli/projects/commands/delete.py similarity index 50% rename from cli/trakcli/projects/commands.py rename to cli/trakcli/projects/commands/delete.py index 95961d9..b9e8ec0 100644 --- a/cli/trakcli/projects/commands.py +++ b/cli/trakcli/projects/commands/delete.py @@ -1,57 +1,15 @@ import pathlib import shutil -from typing import Annotated, Optional import typer from rich import print as rprint from rich.panel import Panel -from rich.table import Table -from trakcli.config.main import ( - TRAK_FOLDER, -) -from trakcli.projects.database import ( - get_projects_from_config, -) +from trakcli.config.main import TRAK_FOLDER from trakcli.utils.print_with_padding import print_with_padding -app = typer.Typer() - -@app.command(help="List your projects.") -def list( - archived: Annotated[ - Optional[bool], - typer.Option( - "--archived", - "-a", - help="Show archived projects in lists.", - ), - ] = False, -): - """List the projects.""" - - projects_in_config = get_projects_from_config(archived) - combined = {*projects_in_config} - - number_of_projects = len(combined) - - table = Table( - title=f"{number_of_projects} Projects", - ) - - table.add_column("id", style="green", no_wrap=True) - table.add_column("from", style="cyan", no_wrap=True) - - for project in projects_in_config: - table.add_row(project, "config") - - rprint("") - rprint(table) - - -@app.command(help="Delete a project.") -def delete(project_id: str): +def command_project_delete(project_id: str): """Delete a project.""" project_path = pathlib.Path(TRAK_FOLDER / "projects" / project_id) diff --git a/cli/trakcli/projects/commands/list.py b/cli/trakcli/projects/commands/list.py new file mode 100644 index 0000000..901939b --- /dev/null +++ b/cli/trakcli/projects/commands/list.py @@ -0,0 +1,38 @@ +from typing import Annotated, Optional + +import typer +from rich import print as rprint +from rich.table import Table + +from trakcli.projects.database import get_projects_from_config + + +def command_project_list( + archived: Annotated[ + Optional[bool], + typer.Option( + "--archived", + "-a", + help="Show archived projects in lists.", + ), + ] = False, +): + """List the projects.""" + + projects_in_config = get_projects_from_config(archived) + combined = {*projects_in_config} + + number_of_projects = len(combined) + + table = Table( + title=f"{number_of_projects} Projects", + ) + + table.add_column("id", style="green", no_wrap=True) + table.add_column("from", style="cyan", no_wrap=True) + + for project in projects_in_config: + table.add_row(project, "config") + + rprint("") + rprint(table) diff --git a/cli/trakcli/projects/database.py b/cli/trakcli/projects/database.py index f910bbb..59234b7 100644 --- a/cli/trakcli/projects/database.py +++ b/cli/trakcli/projects/database.py @@ -3,6 +3,13 @@ import pathlib from trakcli.config.main import TRAK_FOLDER +from trakcli.config.models import Project + +from rich import print as rprint + +from trakcli.projects.messages.print_project_broken_configuration import ( + print_project_broken_configuration, +) def get_projects_from_db(db_path: Path): @@ -35,7 +42,7 @@ def get_projects_from_config(archived: bool | None = False): return projects -def get_project_from_config(project_id: str): +def db_get_project_details(project_id: str) -> Project | None: """Get a project in the config by id.""" project_path = pathlib.Path(TRAK_FOLDER / "projects" / project_id) @@ -44,6 +51,23 @@ def get_project_from_config(project_id: str): details_path = project_path / "details.json" with open(details_path, "r") as f: details = json.load(f) - return details + try: + project = Project(**details) + except Exception: + print_project_broken_configuration(project_id) + return None + + return project + else: + return None + + +def db_get_project_details_path(project_id: str): + """Get project config path.""" + + project_path = pathlib.Path(TRAK_FOLDER / "projects" / project_id) + + if project_path.exists() and project_path.is_dir(): + return project_path / "details.json" else: return None diff --git a/cli/trakcli/projects/messages/print_project_archived_toggle.py b/cli/trakcli/projects/messages/print_project_archived_toggle.py new file mode 100644 index 0000000..14316ab --- /dev/null +++ b/cli/trakcli/projects/messages/print_project_archived_toggle.py @@ -0,0 +1,34 @@ +from rich import print as rprint +from rich.panel import Panel + +from trakcli.utils.print_with_padding import print_with_padding + + +def print_project_archived_toggle(project_id: str, archived: bool): + rprint("") + if archived: + rprint( + Panel.fit( + title=f"[green] The project {project_id} has been archived", + renderable=print_with_padding( + ( + "From now on this project won't be accessible from lists.\n\n" + "[orange3]⭐Tip:[/orange3]\n" + f"You can run trak [orange3]project archive {project_id}[/orange3] to unarchive it." + ) + ), + ) + ) + else: + rprint( + Panel.fit( + title=f"[green]󱝢 The project {project_id} has been unarchived", + renderable=print_with_padding( + ( + "From now on this project will be accessible from lists.\n\n" + "[orange3]⭐Tip:[/orange3]\n" + f"You can run trak [orange3]project archive {project_id}[/orange3] to archive it." + ) + ), + ) + ) diff --git a/cli/trakcli/projects/messages/print_project_broken_configuration.py b/cli/trakcli/projects/messages/print_project_broken_configuration.py new file mode 100644 index 0000000..bad2d44 --- /dev/null +++ b/cli/trakcli/projects/messages/print_project_broken_configuration.py @@ -0,0 +1,16 @@ +from rich import print as rprint +from rich.panel import Panel + +from trakcli.utils.print_with_padding import print_with_padding + + +def print_project_broken_configuration(project_id: str): + rprint("") + rprint( + Panel.fit( + title=f"[red]The project {project_id} has broken configuration", + renderable=print_with_padding( + ("Please, check the details.json file in your project folder.") + ), + ) + ) diff --git a/cli/trakcli/tracker/messages/print_session_already_started.py b/cli/trakcli/tracker/messages/print_session_already_started.py index bdb6c94..9c15952 100644 --- a/cli/trakcli/tracker/messages/print_session_already_started.py +++ b/cli/trakcli/tracker/messages/print_session_already_started.py @@ -18,7 +18,7 @@ def print_session_already_started(record: Record): formatted_start_time = start_datetime.strftime("%Y-%m-%d, %H:%M") msg = ( - f"Tracking on [bold green]{record.project}[/bold green] " + f"Tracking on [bold green]{record.project}[/bold green]" f"already started at {formatted_start_time}.\n\n" f"It's been going for [bold green]{h}h {m}m[/bold green]." ) diff --git a/cli/trakcli/works/commands/list.py b/cli/trakcli/works/commands/list.py index 64e083b..3b30b72 100644 --- a/cli/trakcli/works/commands/list.py +++ b/cli/trakcli/works/commands/list.py @@ -7,7 +7,7 @@ from rich import print as rprint from rich.table import Table -from trakcli.projects.database import get_project_from_config, get_projects_from_config +from trakcli.projects.database import db_get_project_details, get_projects_from_config from trakcli.works.database import get_project_works_from_config ALL_PROJECTS = "all" @@ -99,7 +99,7 @@ def list_works( """List the works in a project or all of them.""" if project_id != ALL_PROJECTS: - details = get_project_from_config(project_id) + details = db_get_project_details(project_id) # Check if project esists if details: