From 64c77d991773ae50b94ad051d0be0dba0bb6fbaf Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 09:52:55 +0200 Subject: [PATCH 01/13] Add dev init command - Also move database commands in the database folder - Use a folder to contain the dev commands --- cli/trakcli/config.py | 2 +- cli/trakcli/database/basic.py | 30 ++++++++++++++++ cli/trakcli/{ => database}/database.py | 17 +++------ cli/trakcli/database/models.py | 10 ++++++ cli/trakcli/dev/commands.py | 49 ++++++++++++++++++++++++++ cli/trakcli/main.py | 8 +++-- 6 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 cli/trakcli/database/basic.py rename cli/trakcli/{ => database}/database.py (95%) create mode 100644 cli/trakcli/database/models.py create mode 100644 cli/trakcli/dev/commands.py diff --git a/cli/trakcli/config.py b/cli/trakcli/config.py index 5d95a72..0f4bfb9 100644 --- a/cli/trakcli/config.py +++ b/cli/trakcli/config.py @@ -4,10 +4,10 @@ # Paths # - TRAK_FOLDER = Path.home() / ".trak" DB_FILE_PATH = TRAK_FOLDER / "db.json" +DEV_DB_FILE_PATH = TRAK_FOLDER / "dev_db.json" CONFIG_FILE_PATH = TRAK_FOLDER / "config.json" diff --git a/cli/trakcli/database/basic.py b/cli/trakcli/database/basic.py new file mode 100644 index 0000000..ffed1ab --- /dev/null +++ b/cli/trakcli/database/basic.py @@ -0,0 +1,30 @@ +import json +from pathlib import Path +from typing import Optional +from rich import print as rprint + + +def show_json_file_content(file_path: Path): + """Show the content of a JSON file.""" + + with open(file_path, "r") as db: + db_content = db.read() + + parsed_json = json.loads(db_content) + rprint(parsed_json) + + +def add_field_to_json_file( + file_path: Path, field_name: Optional[str], field_value: str | int | float | bool +): + """Add a field to a JSON file.""" + + with open(file_path, "r") as db: + db_content = db.read() + + parsed_json = json.loads(db_content) + if field_name: + parsed_json[field_name] = field_value + + with open(file_path, "w") as db: + json.dump(parsed_json, db, indent=2, separators=(",", ": ")) diff --git a/cli/trakcli/database.py b/cli/trakcli/database/database.py similarity index 95% rename from cli/trakcli/database.py rename to cli/trakcli/database/database.py index 1fc2baa..3657064 100644 --- a/cli/trakcli/database.py +++ b/cli/trakcli/database/database.py @@ -1,12 +1,12 @@ import json from datetime import datetime, timedelta from pathlib import Path -from typing import NamedTuple from rich import padding, print as rprint from rich.panel import Panel from trakcli.config import DB_FILE_PATH +from trakcli.database.models import Record from trakcli.utils.format_date import format_date from trakcli.utils.print_with_padding import print_with_padding from rich.console import Console @@ -19,17 +19,8 @@ # -class Record(NamedTuple): - project: str = "" - start: str = "" - end: str = "" - billable: bool = False - category: str = "" - tag: str = "" - - def add_track_field(record: Record): - """...""" + """Add a new session.""" with open(DB_FILE_PATH, "r") as db: db_content = db.read() @@ -228,13 +219,13 @@ def check_if_database_exists(): return Path.exists(DB_FILE_PATH) -def init_database(p: Path) -> int: +def init_database(p: Path, initial_value: str = "[]") -> int: """Create the application database.""" try: p.parent.mkdir(parents=True, exist_ok=True) with p.open("w", encoding="utf-8") as f: - f.write("[]") + f.write(initial_value) return 0 except OSError: return 1 diff --git a/cli/trakcli/database/models.py b/cli/trakcli/database/models.py new file mode 100644 index 0000000..7ad2091 --- /dev/null +++ b/cli/trakcli/database/models.py @@ -0,0 +1,10 @@ +from typing import NamedTuple + + +class Record(NamedTuple): + project: str = "" + start: str = "" + end: str = "" + billable: bool = False + category: str = "" + tag: str = "" diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py new file mode 100644 index 0000000..8d03917 --- /dev/null +++ b/cli/trakcli/dev/commands.py @@ -0,0 +1,49 @@ +from rich.padding import Padding +import typer +from trakcli.config import CONFIG_FILE_PATH, DEV_DB_FILE_PATH + +from trakcli.database.basic import add_field_to_json_file, show_json_file_content +from rich import print as rprint +from rich.prompt import Confirm + +from trakcli.database.database import init_database +from trakcli.utils.print_with_padding import print_with_padding + +app = typer.Typer() + + +@app.command() +def init(): + """Manage the development mode.""" + + rprint(print_with_padding("▶️ Init dev mode!")) + + confirm_reset_development_database = Confirm.ask( + """Are you sure you want to init your development database? +⚠️ If you already have one this command will [bold]delete all your data[/bold].""", + default=True, + ) + + if confirm_reset_development_database: + # Create the development database (dev_db.json) + init_database(DEV_DB_FILE_PATH, "{}") + + rprint( + Padding( + f"✅ Create development database at {DEV_DB_FILE_PATH}", (2, 0, 0, 0) + ) + ) + + # Add the development parameter to config.json + add_field_to_json_file(CONFIG_FILE_PATH, "development", True) + + rprint(f"✅ Add the development parameter to {CONFIG_FILE_PATH}") + + rprint(print_with_padding("⚙️ Here is your new configuration", x=0)) + show_json_file_content(CONFIG_FILE_PATH) + + rprint(print_with_padding("🟢 You are ready to develop on trak!")) + + +if __name__ == "__main__": + app() diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index a9ad970..07ae2d9 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -10,8 +10,7 @@ from trakcli import __app_name__, __version__ from trakcli.config import CONFIG_FILE_PATH, DB_FILE_PATH, init_config -from trakcli.database import ( - Record, +from trakcli.database.database import ( add_track_field, get_current_session, get_record_collection, @@ -19,12 +18,17 @@ stop_track_field, tracking_already_started, ) +from trakcli.database.models import Record from trakcli.utils.print_with_padding import print_with_padding +from trakcli.dev.commands import app as dev_app console = Console() app = typer.Typer() +app.add_typer(dev_app, name="dev") + + initialized = False messages: list[str] = [] From 91ac1816ada4d64945d98bb16011e7625528250f Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 10:10:02 +0200 Subject: [PATCH 02/13] Update function comment --- cli/trakcli/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/trakcli/config.py b/cli/trakcli/config.py index 0f4bfb9..55974a3 100644 --- a/cli/trakcli/config.py +++ b/cli/trakcli/config.py @@ -17,7 +17,7 @@ def init_config(p: Path) -> int: - """Create the to-do database.""" + """Init the config file.""" try: p.parent.mkdir(parents=True, exist_ok=True) From f945016883aff2e07a593b9db27928c7acdaed22 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 10:46:36 +0200 Subject: [PATCH 03/13] Add config commands - Move config.py in a dedicated folder alongside config commands --- cli/trakcli/config/__init__.py | 0 cli/trakcli/config/commands.py | 27 +++++++++++++++++++++++ cli/trakcli/{config.py => config/main.py} | 0 cli/trakcli/database/__init__.py | 0 cli/trakcli/database/basic.py | 7 ++++++ cli/trakcli/database/database.py | 2 +- cli/trakcli/dev/commands.py | 7 +++--- cli/trakcli/main.py | 8 ++++++- 8 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 cli/trakcli/config/__init__.py create mode 100644 cli/trakcli/config/commands.py rename cli/trakcli/{config.py => config/main.py} (100%) create mode 100644 cli/trakcli/database/__init__.py diff --git a/cli/trakcli/config/__init__.py b/cli/trakcli/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/trakcli/config/commands.py b/cli/trakcli/config/commands.py new file mode 100644 index 0000000..5f5398e --- /dev/null +++ b/cli/trakcli/config/commands.py @@ -0,0 +1,27 @@ +import json +from rich.json import JSON +from rich.panel import Panel +import typer + +from trakcli.config.main import CONFIG_FILE_PATH +from trakcli.database.basic import get_json_file_content +from rich import print as rprint + + +app = typer.Typer() + + +@app.command() +def show(): + """Show the config file.""" + + rprint( + Panel( + title=f"Your config file {CONFIG_FILE_PATH}", + renderable=JSON(json.dumps(get_json_file_content(CONFIG_FILE_PATH))), + ) + ) + + +if __name__ == "__main__": + app() diff --git a/cli/trakcli/config.py b/cli/trakcli/config/main.py similarity index 100% rename from cli/trakcli/config.py rename to cli/trakcli/config/main.py diff --git a/cli/trakcli/database/__init__.py b/cli/trakcli/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/trakcli/database/basic.py b/cli/trakcli/database/basic.py index ffed1ab..0a16f84 100644 --- a/cli/trakcli/database/basic.py +++ b/cli/trakcli/database/basic.py @@ -4,6 +4,13 @@ from rich import print as rprint +def get_json_file_content(file_path: Path): + with open(file_path, "r") as db: + db_content = db.read() + + return json.loads(db_content) + + def show_json_file_content(file_path: Path): """Show the content of a JSON file.""" diff --git a/cli/trakcli/database/database.py b/cli/trakcli/database/database.py index 3657064..5aa9c56 100644 --- a/cli/trakcli/database/database.py +++ b/cli/trakcli/database/database.py @@ -4,8 +4,8 @@ from rich import padding, print as rprint from rich.panel import Panel +from trakcli.config.main import DB_FILE_PATH -from trakcli.config import DB_FILE_PATH from trakcli.database.models import Record from trakcli.utils.format_date import format_date from trakcli.utils.print_with_padding import print_with_padding diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index 8d03917..4e689b7 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -1,11 +1,10 @@ -from rich.padding import Padding import typer -from trakcli.config import CONFIG_FILE_PATH, DEV_DB_FILE_PATH - -from trakcli.database.basic import add_field_to_json_file, show_json_file_content from rich import print as rprint +from rich.padding import Padding from rich.prompt import Confirm +from trakcli.config.main import CONFIG_FILE_PATH, DEV_DB_FILE_PATH +from trakcli.database.basic import add_field_to_json_file, show_json_file_content from trakcli.database.database import init_database from trakcli.utils.print_with_padding import print_with_padding diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index 07ae2d9..e291772 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -9,7 +9,8 @@ from typing_extensions import Annotated from trakcli import __app_name__, __version__ -from trakcli.config import CONFIG_FILE_PATH, DB_FILE_PATH, init_config +from trakcli.config.main import init_config, CONFIG_FILE_PATH, DB_FILE_PATH +from trakcli.database.basic import get_json_file_content from trakcli.database.database import ( add_track_field, get_current_session, @@ -21,12 +22,17 @@ from trakcli.database.models import Record from trakcli.utils.print_with_padding import print_with_padding from trakcli.dev.commands import app as dev_app +from trakcli.config.commands import app as config_app console = Console() app = typer.Typer() app.add_typer(dev_app, name="dev") +app.add_typer(config_app, name="config") + +# Read the config at CONFIG_FILE_PATH +config = get_json_file_content(CONFIG_FILE_PATH) initialized = False From f3dd62974aa8e84647bf54242bcb70e33c06139d Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 11:15:43 +0200 Subject: [PATCH 04/13] Refactor --- cli/trakcli/__init__.py | 2 ++ cli/trakcli/initialize.py | 40 ++++++++++++++++++++++++++ cli/trakcli/main.py | 59 +++++++++++++-------------------------- 3 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 cli/trakcli/initialize.py diff --git a/cli/trakcli/__init__.py b/cli/trakcli/__init__.py index a1a1ddd..92c0c43 100644 --- a/cli/trakcli/__init__.py +++ b/cli/trakcli/__init__.py @@ -1,2 +1,4 @@ __app_name__ = "trak" __version__ = "0.0.2" +__website__ = "https://usetrak.com" +__git_repository__ = "https://github.com/lcfd/trak" diff --git a/cli/trakcli/initialize.py b/cli/trakcli/initialize.py new file mode 100644 index 0000000..24e59bf --- /dev/null +++ b/cli/trakcli/initialize.py @@ -0,0 +1,40 @@ +from rich.panel import Panel +from trakcli.config.main import CONFIG_FILE_PATH, DB_FILE_PATH, init_config +from trakcli.database.database import init_database +from rich import print as rprint + +from trakcli.utils.print_with_padding import print_with_padding + + +def initialize_trak(): + """Initialize trak required files and configurations + at the first start.""" + + initialized = False + messages: list[str] = [] + + if not DB_FILE_PATH.is_file(): + initialized = True + try: + init_database(DB_FILE_PATH) + messages.append(f"✅ Database created at {DB_FILE_PATH}.") + except Exception as e: + raise e + + if not CONFIG_FILE_PATH.is_file(): + initialized = True + try: + init_config(CONFIG_FILE_PATH) + messages.append(f"✅ Config file created at {CONFIG_FILE_PATH}.") + except Exception as e: + raise e + + if initialized: + rprint(print_with_padding(text="\n".join(messages), y=1)) + initialized_message = "Trak has created all the files it needs to work." + rprint( + Panel( + print_with_padding(initialized_message, y=2), + title="Trak initalized", + ) + ) diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index e291772..f51832d 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -8,26 +8,29 @@ from rich.panel import Panel from typing_extensions import Annotated -from trakcli import __app_name__, __version__ -from trakcli.config.main import init_config, CONFIG_FILE_PATH, DB_FILE_PATH +from trakcli import __app_name__, __version__, __website__ +from trakcli.config.commands import app as config_app +from trakcli.config.main import CONFIG_FILE_PATH from trakcli.database.basic import get_json_file_content from trakcli.database.database import ( add_track_field, get_current_session, get_record_collection, - init_database, stop_track_field, tracking_already_started, ) from trakcli.database.models import Record -from trakcli.utils.print_with_padding import print_with_padding from trakcli.dev.commands import app as dev_app -from trakcli.config.commands import app as config_app +from trakcli.initialize import initialize_trak +from trakcli.utils.print_with_padding import print_with_padding console = Console() app = typer.Typer() +# Initialize trak required files and settings +initialize_trak() + app.add_typer(dev_app, name="dev") app.add_typer(config_app, name="config") @@ -35,37 +38,6 @@ config = get_json_file_content(CONFIG_FILE_PATH) -initialized = False - -messages: list[str] = [] - -if not DB_FILE_PATH.is_file(): - initialized = True - try: - init_database(DB_FILE_PATH) - messages.append(f"✅ Database created at {DB_FILE_PATH}.") - except Exception as e: - raise e - -if not CONFIG_FILE_PATH.is_file(): - initialized = True - try: - init_config(CONFIG_FILE_PATH) - messages.append(f"✅ Config file created at {CONFIG_FILE_PATH}.") - except Exception as e: - raise e - -if initialized: - rprint(print_with_padding(text="\n".join(messages), y=1)) - initialized_message = "Trak has created all the files it needs to work." - rprint( - Panel( - print_with_padding(initialized_message, y=2), - title="Trak initalized", - ) - ) - - def _version_callback(value: bool) -> None: """ Print the application version. @@ -86,7 +58,7 @@ def _website_callback(value: bool) -> None: Launch the usetrak.com website. """ if value: - typer.launch("https://usetrak.com") + typer.launch(__website__) raise typer.Exit() @@ -242,13 +214,17 @@ def status( h, m = divmod(m, 60) if starship: - print(f"""⏰ {current_session['project']} ⌛ {h}h {m}m""") + print( + f"""⏰ {'( DEV MODE) ' if config['development'] else ''}\ +{current_session['project']} ⌛ {h}h {m}m""" + ) else: rprint( Panel( title="💬 Current status", renderable=print_with_padding( - f"""Project: [bold]{current_session['project']}[/bold] + f"""{'( DEV MODE) ' if config['development'] else ''} +Project: [bold]{current_session['project']}[/bold] Started: {formatted_start_datetime} Time: [bold]{h}h {m}m[/bold]""", ), @@ -256,7 +232,10 @@ def status( ) else: if starship: - print("⏰ No active session") + print( + f"⏰ {'( DEV MODE) ' if config['development'] else ''}\ +No active session" + ) else: rprint( Panel( From 83e3dc30b6cc34793546e97923e2844349c29392 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 11:31:30 +0200 Subject: [PATCH 05/13] Add toggle command skeleton and improve help texts --- cli/trakcli/database/basic.py | 7 +++---- cli/trakcli/dev/commands.py | 17 +++++++++++++---- cli/trakcli/main.py | 10 ++++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/cli/trakcli/database/basic.py b/cli/trakcli/database/basic.py index 0a16f84..75e7bc2 100644 --- a/cli/trakcli/database/basic.py +++ b/cli/trakcli/database/basic.py @@ -1,6 +1,5 @@ import json from pathlib import Path -from typing import Optional from rich import print as rprint @@ -21,10 +20,10 @@ def show_json_file_content(file_path: Path): rprint(parsed_json) -def add_field_to_json_file( - file_path: Path, field_name: Optional[str], field_value: str | int | float | bool +def manage_field_in_json_file( + file_path: Path, field_name: str, field_value: str | int | float | bool ): - """Add a field to a JSON file.""" + """Manage the content of a single object JSON file.""" with open(file_path, "r") as db: db_content = db.read() diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index 4e689b7..e4ce92c 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -4,16 +4,18 @@ from rich.prompt import Confirm from trakcli.config.main import CONFIG_FILE_PATH, DEV_DB_FILE_PATH -from trakcli.database.basic import add_field_to_json_file, show_json_file_content +from trakcli.database.basic import manage_field_in_json_file, show_json_file_content from trakcli.database.database import init_database from trakcli.utils.print_with_padding import print_with_padding app = typer.Typer() -@app.command() +@app.command( + help="Initialize the development database and activate the development mode." +) def init(): - """Manage the development mode.""" + """Initialize the development mode.""" rprint(print_with_padding("▶️ Init dev mode!")) @@ -34,7 +36,7 @@ def init(): ) # Add the development parameter to config.json - add_field_to_json_file(CONFIG_FILE_PATH, "development", True) + manage_field_in_json_file(CONFIG_FILE_PATH, "development", True) rprint(f"✅ Add the development parameter to {CONFIG_FILE_PATH}") @@ -44,5 +46,12 @@ def init(): rprint(print_with_padding("🟢 You are ready to develop on trak!")) +@app.command(help="Toggle the development mode.") +def toggle(): + """Toggle the development mode.""" + + pass + + if __name__ == "__main__": app() diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index f51832d..80af06b 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -31,8 +31,10 @@ # Initialize trak required files and settings initialize_trak() -app.add_typer(dev_app, name="dev") -app.add_typer(config_app, name="config") +app.add_typer( + dev_app, name="dev", help="Utils for developers who wants to work on trak." +) +app.add_typer(config_app, name="config", help="Interact with your configuration.") # Read the config at CONFIG_FILE_PATH config = get_json_file_content(CONFIG_FILE_PATH) @@ -85,7 +87,7 @@ def main( return -@app.command(name="start", short_help="Start trak") +@app.command(name="start", help="Start a trak session.") def start_tracker( project: str, billable: Annotated[ @@ -156,7 +158,7 @@ def start_tracker( ) -@app.command("stop", short_help="Stop trak") +@app.command("stop", help="Stop the current trak session.") def stop_tracker(): """ Stop tracking the current project. From 5dab4ed58ff3541820db54edb1fde6e2367bd4f0 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 11:48:42 +0200 Subject: [PATCH 06/13] Add dev toggle functionality - Move config to CONFIG in the config module --- cli/trakcli/config/main.py | 4 ++++ cli/trakcli/dev/commands.py | 23 +++++++++++++++++++++-- cli/trakcli/main.py | 13 +++++-------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/cli/trakcli/config/main.py b/cli/trakcli/config/main.py index 55974a3..1a48082 100644 --- a/cli/trakcli/config/main.py +++ b/cli/trakcli/config/main.py @@ -1,5 +1,7 @@ from pathlib import Path +from trakcli.database.basic import get_json_file_content + # # Paths # @@ -10,6 +12,8 @@ DEV_DB_FILE_PATH = TRAK_FOLDER / "dev_db.json" CONFIG_FILE_PATH = TRAK_FOLDER / "config.json" +# Read the config at CONFIG_FILE_PATH +CONFIG = get_json_file_content(CONFIG_FILE_PATH) # # Configuration helpers diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index e4ce92c..a8df70f 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -3,7 +3,7 @@ from rich.padding import Padding from rich.prompt import Confirm -from trakcli.config.main import CONFIG_FILE_PATH, DEV_DB_FILE_PATH +from trakcli.config.main import CONFIG, CONFIG_FILE_PATH, DEV_DB_FILE_PATH from trakcli.database.basic import manage_field_in_json_file, show_json_file_content from trakcli.database.database import init_database from trakcli.utils.print_with_padding import print_with_padding @@ -50,7 +50,26 @@ def init(): def toggle(): """Toggle the development mode.""" - pass + if "development" in CONFIG: + manage_field_in_json_file( + CONFIG_FILE_PATH, "development", not CONFIG["development"] + ) + if not CONFIG["development"]: + rprint(print_with_padding("🟢 You are ready to develop on trak!")) + else: + rprint( + print_with_padding( + """👋 You exited the development mode. + +Thanks for your help 🙏""" + ) + ) + else: + rprint( + print_with_padding( + "🔴 You have to run the [bold]trak dev init[/bold] command." + ) + ) if __name__ == "__main__": diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index 80af06b..0d1905e 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -10,8 +10,7 @@ from trakcli import __app_name__, __version__, __website__ from trakcli.config.commands import app as config_app -from trakcli.config.main import CONFIG_FILE_PATH -from trakcli.database.basic import get_json_file_content +from trakcli.config.main import CONFIG from trakcli.database.database import ( add_track_field, get_current_session, @@ -31,14 +30,12 @@ # Initialize trak required files and settings initialize_trak() +# Add subcommands app.add_typer( dev_app, name="dev", help="Utils for developers who wants to work on trak." ) app.add_typer(config_app, name="config", help="Interact with your configuration.") -# Read the config at CONFIG_FILE_PATH -config = get_json_file_content(CONFIG_FILE_PATH) - def _version_callback(value: bool) -> None: """ @@ -217,7 +214,7 @@ def status( if starship: print( - f"""⏰ {'( DEV MODE) ' if config['development'] else ''}\ + f"""⏰ {'( DEV MODE) ' if CONFIG['development'] else ''}\ {current_session['project']} ⌛ {h}h {m}m""" ) else: @@ -225,7 +222,7 @@ def status( Panel( title="💬 Current status", renderable=print_with_padding( - f"""{'( DEV MODE) ' if config['development'] else ''} + f"""{'( DEV MODE) ' if CONFIG['development'] else ''} Project: [bold]{current_session['project']}[/bold] Started: {formatted_start_datetime} Time: [bold]{h}h {m}m[/bold]""", @@ -235,7 +232,7 @@ def status( else: if starship: print( - f"⏰ {'( DEV MODE) ' if config['development'] else ''}\ + f"⏰ {'( DEV MODE) ' if CONFIG['development'] else ''}\ No active session" ) else: From 75e982fc8c73213e4dafe84602af8563b29e23c4 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 12:10:23 +0200 Subject: [PATCH 07/13] Add dev options to root command - move callbacks to a dedicated file --- cli/trakcli/callbacks.py | 57 ++++++++++++++++++++++++++++++++++ cli/trakcli/dev/__init__.py | 0 cli/trakcli/main.py | 62 ++++++++++++++++++++----------------- 3 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 cli/trakcli/callbacks.py create mode 100644 cli/trakcli/dev/__init__.py diff --git a/cli/trakcli/callbacks.py b/cli/trakcli/callbacks.py new file mode 100644 index 0000000..13926d4 --- /dev/null +++ b/cli/trakcli/callbacks.py @@ -0,0 +1,57 @@ +import typer +from rich import print as rprint +from rich.align import Align +from rich.panel import Panel + +from trakcli.__init__ import __app_name__, __git_repository__, __version__, __website__ + + +def version_callback(value: bool) -> None: + """ + Print the application version. + """ + if value: + rprint( + Panel( + renderable=Align.center(f"{__app_name__} v{__version__}"), + title=__app_name__, + padding=(2), + ), + ) + raise typer.Exit() + + +def website_callback(value: bool) -> None: + """ + Launch the usetrak.com website. + """ + if value: + typer.launch(__website__) + raise typer.Exit() + + +def repository_callback(value: bool) -> None: + """ + Launch the usetrak.com website. + """ + if value: + typer.launch(__git_repository__) + raise typer.Exit() + + +def issues_callback(value: bool) -> None: + """ + Launch issues page. + """ + if value: + typer.launch("https://github.com/lcfd/trak/issues") + raise typer.Exit() + + +def report_bug_callback(value: bool) -> None: + """ + Launch report bug page. + """ + if value: + typer.launch("https://github.com/lcfd/trak/issues/new") + raise typer.Exit() diff --git a/cli/trakcli/dev/__init__.py b/cli/trakcli/dev/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index 0d1905e..8bab5d1 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -3,12 +3,17 @@ import typer from rich import print as rprint -from rich.align import Align from rich.console import Console from rich.panel import Panel from typing_extensions import Annotated -from trakcli import __app_name__, __version__, __website__ +from trakcli.callbacks import ( + issues_callback, + report_bug_callback, + repository_callback, + version_callback, + website_callback, +) from trakcli.config.commands import app as config_app from trakcli.config.main import CONFIG from trakcli.database.database import ( @@ -37,31 +42,6 @@ app.add_typer(config_app, name="config", help="Interact with your configuration.") -def _version_callback(value: bool) -> None: - """ - Print the application version. - """ - if value: - rprint( - Panel( - renderable=Align.center(f"{__app_name__} v{__version__}"), - title=__app_name__, - padding=(2), - ), - ) - raise typer.Exit() - - -def _website_callback(value: bool) -> None: - """ - Launch the usetrak.com website. - """ - if value: - typer.launch(__website__) - - raise typer.Exit() - - @app.callback() def main( version: Optional[bool] = typer.Option( @@ -69,7 +49,7 @@ def main( "--version", "-v", help="Show the application's version and exit.", - callback=_version_callback, + callback=version_callback, is_eager=True, ), website: Optional[bool] = typer.Option( @@ -77,7 +57,31 @@ def main( "--website", "-w", help="Launch the usetrak.com website.", - callback=_website_callback, + callback=website_callback, + is_eager=True, + ), + repository: Optional[bool] = typer.Option( + None, + "--repository", + "-r", + help="Launch the trak repository.", + callback=repository_callback, + is_eager=True, + ), + issues: Optional[bool] = typer.Option( + None, + "--issues", + "-i", + help="Launch the trak issues page.", + callback=issues_callback, + is_eager=True, + ), + bug: Optional[bool] = typer.Option( + None, + "--bug", + "-b", + help="Report a bug", + callback=report_bug_callback, is_eager=True, ), ) -> None: From 5c81ac58f378dc0a3459d5c1719f556038057515 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 12:56:35 +0200 Subject: [PATCH 08/13] Use the dev db if in development mode - Fix some help text --- cli/trakcli/database/database.py | 28 +++++++++++++++++----------- cli/trakcli/dev/commands.py | 2 +- cli/trakcli/main.py | 19 ++++++++++--------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/cli/trakcli/database/database.py b/cli/trakcli/database/database.py index 5aa9c56..cc61beb 100644 --- a/cli/trakcli/database/database.py +++ b/cli/trakcli/database/database.py @@ -4,7 +4,7 @@ from rich import padding, print as rprint from rich.panel import Panel -from trakcli.config.main import DB_FILE_PATH +from trakcli.config.main import CONFIG, DB_FILE_PATH, DEV_DB_FILE_PATH from trakcli.database.models import Record from trakcli.utils.format_date import format_date @@ -14,34 +14,36 @@ from trakcli.utils.same_week import same_week +file_path_to_use = DEV_DB_FILE_PATH if CONFIG["development"] else DB_FILE_PATH + # # Database operations # -def add_track_field(record: Record): +def add_session(record: Record): """Add a new session.""" - with open(DB_FILE_PATH, "r") as db: + with open(file_path_to_use, "r") as db: db_content = db.read() parsed_json = json.loads(db_content) parsed_json.append(record._asdict()) - with open(DB_FILE_PATH, "w") as db: + with open(file_path_to_use, "w") as db: json.dump(parsed_json, db, indent=2, separators=(",", ": ")) -def stop_track_field(): +def stop_trak_session(): """Stop tracking the current project.""" - with open(DB_FILE_PATH, "r") as db: + with open(file_path_to_use, "r") as db: db_content = db.read() parsed_json = json.loads(db_content) parsed_json[-1]["end"] = datetime.now().isoformat() - with open(DB_FILE_PATH, "w") as db: + with open(file_path_to_use, "w") as db: json.dump(parsed_json, db, indent=2, separators=(",", ": ")) @@ -51,7 +53,7 @@ def tracking_already_started(): If it's already running return the record. """ - with open(DB_FILE_PATH, "r") as db: + with open(file_path_to_use, "r") as db: db_content = db.read() parsed_json = json.loads(db_content) @@ -59,6 +61,8 @@ def tracking_already_started(): last_record = parsed_json[-1] except IndexError: return False + except KeyError: + return False if last_record["end"] == "": return last_record @@ -67,7 +71,7 @@ def tracking_already_started(): def get_current_session(): - with open(DB_FILE_PATH, "r") as db: + with open(file_path_to_use, "r") as db: db_content = db.read() parsed_json = json.loads(db_content) @@ -76,6 +80,8 @@ def get_current_session(): last_record = parsed_json[-1] except IndexError: return False + except KeyError: + return False if last_record["end"] == "": return last_record @@ -92,7 +98,7 @@ def get_record_collection( ): """Get a collection of records, filtered by paramenters.""" - with open(DB_FILE_PATH, "r") as db: + with open(file_path_to_use, "r") as db: db_content = db.read() parsed_json = json.loads(db_content) @@ -216,7 +222,7 @@ def trunc_datetime(someDate): def check_if_database_exists(): """Check if the json db files exists.""" - return Path.exists(DB_FILE_PATH) + return Path.exists(file_path_to_use) def init_database(p: Path, initial_value: str = "[]") -> int: diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index a8df70f..526c7f0 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -27,7 +27,7 @@ def init(): if confirm_reset_development_database: # Create the development database (dev_db.json) - init_database(DEV_DB_FILE_PATH, "{}") + init_database(DEV_DB_FILE_PATH, "[]") rprint( Padding( diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index 8bab5d1..6e9b1a6 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -17,10 +17,10 @@ from trakcli.config.commands import app as config_app from trakcli.config.main import CONFIG from trakcli.database.database import ( - add_track_field, + add_session, get_current_session, get_record_collection, - stop_track_field, + stop_trak_session, tracking_already_started, ) from trakcli.database.models import Record @@ -48,7 +48,7 @@ def main( None, "--version", "-v", - help="Show the application's version and exit.", + help="Show the application's version.", callback=version_callback, is_eager=True, ), @@ -72,7 +72,7 @@ def main( None, "--issues", "-i", - help="Launch the trak issues page.", + help="Launch the trak issues page on Github.", callback=issues_callback, is_eager=True, ), @@ -80,7 +80,7 @@ def main( None, "--bug", "-b", - help="Report a bug", + help="Report a bug on Github.", callback=report_bug_callback, is_eager=True, ), @@ -127,7 +127,7 @@ def start_tracker( record = tracking_already_started() if not record: - add_track_field( + add_session( Record( project=project, start=datetime.now().isoformat(), @@ -152,7 +152,7 @@ def start_tracker( title="💬 Already started", renderable=print_with_padding( f""" -Tracking on [bold green]{project}[/bold green] already started \ +Tracking on [bold green]{record['project']}[/bold green] already started \ at {datetime.fromisoformat(record['start']).strftime("%m/%d/%Y, %H:%M")}""" ), ) @@ -167,7 +167,7 @@ def stop_tracker(): record = tracking_already_started() if record: - stop_track_field() + stop_trak_session() message = print_with_padding( f""" The [bold green]{record['project']}[/bold green] session is over. @@ -244,7 +244,8 @@ def status( Panel( title="💬 No active session", renderable=print_with_padding( - """Ther aren't active sessions. + f"""{'( DEV MODE) ' if CONFIG['development'] else ''} \ +Ther aren't active sessions. Use the command: trak start to start a new session of work.""" ), From ecf302a49ed36058741a47f78aa648b05a31bc6a Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 14:33:57 +0200 Subject: [PATCH 09/13] Add the dev fake feature --- cli/trakcli/database/basic.py | 7 ++++++ cli/trakcli/database/models.py | 10 ++++++++ cli/trakcli/dev/commands.py | 46 +++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/cli/trakcli/database/basic.py b/cli/trakcli/database/basic.py index 75e7bc2..42009eb 100644 --- a/cli/trakcli/database/basic.py +++ b/cli/trakcli/database/basic.py @@ -34,3 +34,10 @@ def manage_field_in_json_file( with open(file_path, "w") as db: json.dump(parsed_json, db, indent=2, separators=(",", ": ")) + + +def overwrite_json_file(file_path: Path, content: dict | list[dict]): + """Fill a JSON file with the provided content. It's a complete overwrite.""" + + with open(file_path, "w") as db: + json.dump(content, db, indent=2, separators=(",", ": ")) diff --git a/cli/trakcli/database/models.py b/cli/trakcli/database/models.py index 7ad2091..38d6d22 100644 --- a/cli/trakcli/database/models.py +++ b/cli/trakcli/database/models.py @@ -8,3 +8,13 @@ class Record(NamedTuple): billable: bool = False category: str = "" tag: str = "" + + +# SPOILER + +# class Project(NamedTuple): +# short_name: str +# name: str = "" +# description: str = "" +# customer: str = "" +# hour_rate: str = "" diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index 526c7f0..3602197 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -1,11 +1,20 @@ +from datetime import datetime, timedelta +from random import randrange +import random + import typer from rich import print as rprint from rich.padding import Padding from rich.prompt import Confirm from trakcli.config.main import CONFIG, CONFIG_FILE_PATH, DEV_DB_FILE_PATH -from trakcli.database.basic import manage_field_in_json_file, show_json_file_content +from trakcli.database.basic import ( + manage_field_in_json_file, + overwrite_json_file, + show_json_file_content, +) from trakcli.database.database import init_database +from trakcli.database.models import Record from trakcli.utils.print_with_padding import print_with_padding app = typer.Typer() @@ -72,5 +81,40 @@ def toggle(): ) +@app.command(help="Produces mock data for testing purposes.") +def fake(amount: int): + """Produces mock data for testing purposes.""" + + fake_records = [] + + today = datetime.now() + past_date = today + + categories = ["frontend", "backend", "meeting"] + tags = ["solo", "multi"] + + for _ in range(0, amount): + delta = timedelta(hours=randrange(1, 6), minutes=randrange(1, 40)) + delta_plus = timedelta(hours=randrange(1, 3)) + + past_date = past_date - delta + past_date_after = past_date + delta_plus + + fake_records.append( + Record( + project="test", + start=past_date.isoformat(), + end=past_date_after.isoformat(), + category=random.choice(categories), + tag=random.choice(tags), + billable=random.choice([True, False]), + )._asdict() + ) + + overwrite_json_file(file_path=DEV_DB_FILE_PATH, content=fake_records) + + rprint(print_with_padding(f"🟢 {amount} fake sessions have been created.")) + + if __name__ == "__main__": app() From f394d9690ee761900d3ca090b3bce9476272e329 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:15:40 +0200 Subject: [PATCH 10/13] New colors for report table --- cli/trakcli/database/database.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/trakcli/database/database.py b/cli/trakcli/database/database.py index cc61beb..a09a27c 100644 --- a/cli/trakcli/database/database.py +++ b/cli/trakcli/database/database.py @@ -176,11 +176,11 @@ def trunc_datetime(someDate): table = Table(title=f"[bold]{project}[/bold]") - table.add_column("Start", justify="right", style="cyan", no_wrap=True) - table.add_column("End", style="cyan", no_wrap=True) - table.add_column("Category", style="magenta") - table.add_column("Tag", style="magenta") - table.add_column("Hours", style="magenta", no_wrap=True) + table.add_column("Start", justify="right", style="green", no_wrap=True) + table.add_column("End", style="orange3", no_wrap=True) + table.add_column("Category", style="steel_blue1") + table.add_column("Tag", style="steel_blue3") + table.add_column("Hours", style="yellow", no_wrap=True) table.add_column("Billable") acc_seconds = 0 From 861d5e25676c18259e2df2cd2c90e7df8d56d01e Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:15:55 +0200 Subject: [PATCH 11/13] Removed dev mode in command ouputs The Starship integration and the config show should be enough. --- cli/trakcli/main.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cli/trakcli/main.py b/cli/trakcli/main.py index 6e9b1a6..e2116e5 100644 --- a/cli/trakcli/main.py +++ b/cli/trakcli/main.py @@ -147,14 +147,17 @@ def start_tracker( ) ) else: + formatted_start_time = datetime.fromisoformat(record["start"]).strftime( + "%m/%d/%Y, %H:%M" + ) + msg = ( + f"Tracking on [bold green]{record['project']}[/bold green] " + f"already started at {formatted_start_time}" + ) rprint( Panel.fit( title="💬 Already started", - renderable=print_with_padding( - f""" -Tracking on [bold green]{record['project']}[/bold green] already started \ -at {datetime.fromisoformat(record['start']).strftime("%m/%d/%Y, %H:%M")}""" - ), + renderable=print_with_padding(msg), ) ) @@ -169,8 +172,7 @@ def stop_tracker(): if record: stop_trak_session() message = print_with_padding( - f""" -The [bold green]{record['project']}[/bold green] session is over. + f"""The [bold green]{record['project']}[/bold green] session is over. Good job!""" ) @@ -181,7 +183,7 @@ def stop_tracker(): Panel.fit( title="💬 No active sessions", renderable=print_with_padding( - """Ther aren't active sessions. + """There aren't active sessions. Use the command: trak start to start a new session of work.""" ), @@ -226,8 +228,7 @@ def status( Panel( title="💬 Current status", renderable=print_with_padding( - f"""{'( DEV MODE) ' if CONFIG['development'] else ''} -Project: [bold]{current_session['project']}[/bold] + f"""Project: [bold]{current_session['project']}[/bold] Started: {formatted_start_datetime} Time: [bold]{h}h {m}m[/bold]""", ), @@ -244,8 +245,7 @@ def status( Panel( title="💬 No active session", renderable=print_with_padding( - f"""{'( DEV MODE) ' if CONFIG['development'] else ''} \ -Ther aren't active sessions. + """Ther aren't active sessions. Use the command: trak start to start a new session of work.""" ), From 40ba2a43ef22fb75c02cbd9c9b564a345f93acd8 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:18:27 +0200 Subject: [PATCH 12/13] Organize imports --- cli/trakcli/__main__.py | 1 - cli/trakcli/config/commands.py | 6 +++--- cli/trakcli/database/database.py | 10 +++++----- cli/trakcli/dev/commands.py | 2 +- cli/trakcli/initialize.py | 4 ++-- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/cli/trakcli/__main__.py b/cli/trakcli/__main__.py index f9e6305..71a7dce 100644 --- a/cli/trakcli/__main__.py +++ b/cli/trakcli/__main__.py @@ -1,5 +1,4 @@ from trakcli import __app_name__ - from trakcli.main import app app(prog_name=__app_name__) diff --git a/cli/trakcli/config/commands.py b/cli/trakcli/config/commands.py index 5f5398e..812f054 100644 --- a/cli/trakcli/config/commands.py +++ b/cli/trakcli/config/commands.py @@ -1,12 +1,12 @@ import json + +import typer +from rich import print as rprint from rich.json import JSON from rich.panel import Panel -import typer from trakcli.config.main import CONFIG_FILE_PATH from trakcli.database.basic import get_json_file_content -from rich import print as rprint - app = typer.Typer() diff --git a/cli/trakcli/database/database.py b/cli/trakcli/database/database.py index a09a27c..846d1b3 100644 --- a/cli/trakcli/database/database.py +++ b/cli/trakcli/database/database.py @@ -2,16 +2,16 @@ from datetime import datetime, timedelta from pathlib import Path -from rich import padding, print as rprint +from rich import padding +from rich import print as rprint +from rich.console import Console from rich.panel import Panel -from trakcli.config.main import CONFIG, DB_FILE_PATH, DEV_DB_FILE_PATH +from rich.table import Table +from trakcli.config.main import CONFIG, DB_FILE_PATH, DEV_DB_FILE_PATH from trakcli.database.models import Record from trakcli.utils.format_date import format_date from trakcli.utils.print_with_padding import print_with_padding -from rich.console import Console -from rich.table import Table - from trakcli.utils.same_week import same_week file_path_to_use = DEV_DB_FILE_PATH if CONFIG["development"] else DB_FILE_PATH diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index 3602197..123c799 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -1,6 +1,6 @@ +import random from datetime import datetime, timedelta from random import randrange -import random import typer from rich import print as rprint diff --git a/cli/trakcli/initialize.py b/cli/trakcli/initialize.py index 24e59bf..bcf3774 100644 --- a/cli/trakcli/initialize.py +++ b/cli/trakcli/initialize.py @@ -1,8 +1,8 @@ +from rich import print as rprint from rich.panel import Panel + from trakcli.config.main import CONFIG_FILE_PATH, DB_FILE_PATH, init_config from trakcli.database.database import init_database -from rich import print as rprint - from trakcli.utils.print_with_padding import print_with_padding From 46d4dfbe6712b783234d9aaeb6ce5005eabb2af4 Mon Sep 17 00:00:00 2001 From: lcfd <9001053+fedriz@users.noreply.github.com> Date: Sat, 14 Oct 2023 17:45:40 +0200 Subject: [PATCH 13/13] Check if dev mode is enabled --- cli/trakcli/dev/commands.py | 61 +++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/cli/trakcli/dev/commands.py b/cli/trakcli/dev/commands.py index 123c799..091c687 100644 --- a/cli/trakcli/dev/commands.py +++ b/cli/trakcli/dev/commands.py @@ -85,35 +85,42 @@ def toggle(): def fake(amount: int): """Produces mock data for testing purposes.""" - fake_records = [] - - today = datetime.now() - past_date = today - - categories = ["frontend", "backend", "meeting"] - tags = ["solo", "multi"] - - for _ in range(0, amount): - delta = timedelta(hours=randrange(1, 6), minutes=randrange(1, 40)) - delta_plus = timedelta(hours=randrange(1, 3)) - - past_date = past_date - delta - past_date_after = past_date + delta_plus - - fake_records.append( - Record( - project="test", - start=past_date.isoformat(), - end=past_date_after.isoformat(), - category=random.choice(categories), - tag=random.choice(tags), - billable=random.choice([True, False]), - )._asdict() - ) + if CONFIG["development"]: + fake_records = [] + + today = datetime.now() + past_date = today + + categories = ["frontend", "backend", "meeting"] + tags = ["solo", "multi"] + + for _ in range(0, amount): + delta = timedelta(hours=randrange(1, 6), minutes=randrange(1, 40)) + delta_plus = timedelta(hours=randrange(1, 3)) + + past_date = past_date - delta + past_date_after = past_date + delta_plus + + fake_records.append( + Record( + project="test", + start=past_date.isoformat(), + end=past_date_after.isoformat(), + category=random.choice(categories), + tag=random.choice(tags), + billable=random.choice([True, False]), + )._asdict() + ) - overwrite_json_file(file_path=DEV_DB_FILE_PATH, content=fake_records) + overwrite_json_file(file_path=DEV_DB_FILE_PATH, content=fake_records) - rprint(print_with_padding(f"🟢 {amount} fake sessions have been created.")) + rprint(print_with_padding(f"🟢 {amount} fake sessions have been created.")) + else: + rprint( + print_with_padding( + "🔴 This command works only if the developer mode is enabled." + ) + ) if __name__ == "__main__":