improve-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
improve.py ADDED
@@ -0,0 +1,129 @@
1
+ """Improve CLI"""
2
+ """Developed by: harshit-malik25813"""
3
+ """Open Sourced on GitHub"""
4
+ """https://github.com/harshit-malik25813/Improve"""
5
+ import argparse # To include the functionality of parsing the arguements
6
+ import json # To Read JSON file containing the user data
7
+ from pathlib import Path # To locate the path of essential tracking data
8
+ # Essential helper functions imported
9
+ from src import helpers, setup as setup_mod, project as project_mod
10
+ from src.help import show_help, commands
11
+
12
+ # User tracking path
13
+ USER_FILE = Path("tracking") / "user_info.json"
14
+
15
+ # Internal function to load user data and return it
16
+ def _load_user():
17
+ if USER_FILE.exists():
18
+ with USER_FILE.open("r", encoding="utf-8") as f:
19
+ return json.load(f)
20
+ return {"user_exists": False, "user_name": "", "password": ""}
21
+
22
+ # Main program
23
+ def main():
24
+ # Adding argument parsers
25
+ parser = argparse.ArgumentParser(prog="improve", description="Improve CLI") # Initiate parsing of arguements
26
+ # Allow subparsers in the program
27
+ subparsers = parser.add_subparsers(dest="command")
28
+
29
+ # setup arguments
30
+ setup_parser = subparsers.add_parser("setup", help="Manage your local user account")
31
+ setup_sub = setup_parser.add_subparsers(dest="setup_cmd", required=True)
32
+ setup_sub.add_parser("login", help="Create or sign in to your local account")
33
+ setup_sub.add_parser("logout", help="Sign out and clear local user data")
34
+ update_parser = setup_sub.add_parser("update", help="Update username or password")
35
+ update_group = update_parser.add_mutually_exclusive_group(required=True)
36
+ update_group.add_argument("--username", action="store_true", help="Change username")
37
+ update_group.add_argument("--password", action="store_true", help="Change password")
38
+
39
+ # help (use "help" not "--help"; argparse reserves --help for usage)
40
+ help_parser = subparsers.add_parser("help", help="Show help or list commands")
41
+ help_parser.add_argument("--commands", action="store_true", help="List all commands")
42
+
43
+ # add-project (legacy) - kept for backward compatibility as the feature has been integrated with project command
44
+ addp = subparsers.add_parser("add-project") # add-project argument
45
+ addp.add_argument("project_name") # Project name
46
+
47
+ # project management group(improved)
48
+ project_parser = subparsers.add_parser("project")
49
+ project_sub = project_parser.add_subparsers(dest="proj_cmd")
50
+ project_sub.add_parser("list")
51
+ create = project_sub.add_parser("create")
52
+ create.add_argument("project_name")
53
+ add_entry = project_sub.add_parser("add-entry")
54
+ add_entry.add_argument("project_name")
55
+ add_entry.add_argument("--day", default="")
56
+ add_entry.add_argument("--date", default="")
57
+ add_entry.add_argument("--progress", default="")
58
+ add_entry.add_argument("--productivity", default="")
59
+ add_entry.add_argument("--feedback", default="")
60
+ feedback = project_sub.add_parser("feedback")
61
+ feedback.add_argument("project_name")
62
+ feedback.add_argument("--last", type=int, default=5)
63
+ show = project_sub.add_parser("show")
64
+ show.add_argument("project_name")
65
+ show.add_argument("--last", type=int, default=10)
66
+ delete = project_sub.add_parser("delete")
67
+ delete.add_argument("project_name")
68
+
69
+ # export
70
+ subparsers.add_parser("export")
71
+
72
+ args = parser.parse_args()
73
+
74
+ user_info = _load_user() # Load user data
75
+ if not user_info.get("user_exists"): # If the user data doesn't exist
76
+ helpers.first_time() # Initiate the script to invite user for the first time
77
+ else:
78
+ print(f"Welcome back!, {user_info.get('user_name')}")
79
+
80
+ if args.command == "setup":
81
+ if args.setup_cmd == "login":
82
+ setup_mod.login()
83
+ elif args.setup_cmd == "logout":
84
+ setup_mod.logout()
85
+ elif args.setup_cmd == "update":
86
+ if args.username:
87
+ setup_mod.update_user("username")
88
+ else:
89
+ setup_mod.update_user("password")
90
+ return
91
+
92
+ if args.command == "help":
93
+ if args.commands:
94
+ commands()
95
+ else:
96
+ show_help()
97
+ return
98
+
99
+ if args.command == "add-project": # Legacy function to create the project
100
+ # legacy behavior: create project CSV
101
+ project_mod.create_project(args.project_name) # Use the create project function in place of add_project.py
102
+ return
103
+ # Project functions
104
+ if args.command == "project":
105
+ if args.proj_cmd == "list":
106
+ project_mod.list_projects()
107
+ elif args.proj_cmd == "create":
108
+ project_mod.create_project(args.project_name)
109
+ elif args.proj_cmd == "add-entry":
110
+ project_mod.add_entry(args.project_name, day=args.day, date=args.date, progress=args.progress, productivity=args.productivity, feedback=args.feedback)
111
+ elif args.proj_cmd == "feedback":
112
+ project_mod.get_feedback(args.project_name, last=args.last)
113
+ elif args.proj_cmd == "show":
114
+ project_mod.show_project(args.project_name, last=args.last)
115
+ elif args.proj_cmd == "delete":
116
+ project_mod.delete_project(args.project_name)
117
+ else:
118
+ show_help()
119
+ return
120
+ # Export functions
121
+ if args.command == "export":
122
+ from src.export import export_data
123
+ export_data()
124
+ return
125
+ # if invalid
126
+ parser.print_help()
127
+ # Initialise the function
128
+ if __name__ == "__main__":
129
+ main()
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: improve-cli
3
+ Version: 0.1.0
4
+ Summary: CLI project tracker with CSV-backed logs
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Dynamic: license-file
9
+
10
+ # **Improve — CLI Project Tracker**
11
+
12
+ Improve is a small, single-file CLI application (with supporting modules under `src/`) to help you track progress on projects using CSV-backed project logs. It provides simple user setup, per-project CSV tracking, quick reporting and export functionality.
13
+
14
+ ## **Features**
15
+ - **User setup:** create, update, and logout a local user account (`setup` commands).
16
+ - **Project management:** create, list, delete projects and add daily entries (`project` commands).
17
+ - **Entry tracking:** each project is stored as a CSV under `tracking/` with columns Day, Date, Progress, Productivity Level, Feedback.
18
+ - **Feedback & view:** fetch recent feedback entries or show recent rows for a project.
19
+ - **Export:** copy all tracking CSV files to a `backup/` folder.
20
+
21
+ ### **Quick Links**
22
+ - Main CLI entry: [improve.py](improve.py)
23
+ - Core project logic: [src/project.py](src/project.py)
24
+ - User/setup utilities: [src/setup.py](src/setup.py)
25
+ - Export helper: [src/export.py](src/export.py)
26
+ - First-run helper text: [src/helpers.py](src/helpers.py)
27
+ - Legacy project helper: [src/add_project.py](src/add_project.py)
28
+
29
+ ## **Requirements**
30
+ - Python 3.9+ (`requires-python` in [pyproject.toml](pyproject.toml); uses standard library features such as `dataclasses` and `pathlib`).
31
+ - No third-party runtime dependencies. The package is defined in [pyproject.toml](pyproject.toml); [requirements.txt](requirements.txt) only lists standard-library modules for reference and is not used by pip.
32
+
33
+ ## **Install with pip**
34
+
35
+ From a clone of this repository (recommended: use a virtual environment):
36
+
37
+ ```bash
38
+ python3 -m venv .venv
39
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
40
+ pip install .
41
+ ```
42
+
43
+ After install, the `improve` console script is on your `PATH` (same commands as `python improve.py` below).
44
+
45
+ For local development (changes to `improve.py` or `src/` apply without reinstalling):
46
+
47
+ ```bash
48
+ pip install -e .
49
+ ```
50
+
51
+ Install a built wheel without cloning:
52
+
53
+ ```bash
54
+ pip install dist/improve_cli-0.1.0-py3-none-any.whl
55
+ ```
56
+
57
+ ## **Build from source**
58
+
59
+ Build sdist and wheel artifacts into `dist/` (requires [build](https://pypi.org/project/build/) once per environment):
60
+
61
+ ```bash
62
+ python3 -m venv .venv
63
+ source .venv/bin/activate
64
+ pip install build
65
+ python -m build
66
+ ```
67
+
68
+ Outputs:
69
+ - `dist/improve_cli-0.1.0.tar.gz` — source distribution
70
+ - `dist/improve_cli-0.1.0-py3-none-any.whl` — installable wheel
71
+
72
+ Install the wheel locally:
73
+
74
+ ```bash
75
+ pip install dist/improve_cli-0.1.0-py3-none-any.whl
76
+ ```
77
+
78
+ ## **Run without installing**
79
+
80
+ Clone the repo, create a venv if you like, and invoke the entry module directly:
81
+
82
+ ```bash
83
+ python improve.py --help
84
+ ```
85
+
86
+ ## **Running the CLI**
87
+ - Basic help:
88
+
89
+ ```bash
90
+ improve --help
91
+ # or: python improve.py --help
92
+ ```
93
+
94
+ - Setup a user account (interactive):
95
+
96
+ ```bash
97
+ improve setup login
98
+ ```
99
+
100
+ - Sign out and clear local user data:
101
+
102
+ ```bash
103
+ improve setup logout
104
+ ```
105
+
106
+ - Update username or password:
107
+
108
+ ```bash
109
+ improve setup update --username
110
+ improve setup update --password
111
+ ```
112
+
113
+ - Create a project:
114
+
115
+ ```bash
116
+ improve project create MyProject
117
+ ```
118
+
119
+ - Add an entry to a project:
120
+
121
+ ```bash
122
+ improve project add-entry MyProject --day "Day 1" --date "2026-05-28" --progress "Started" --productivity "7" --feedback "Good start"
123
+ ```
124
+
125
+ - List projects:
126
+
127
+ ```bash
128
+ improve project list
129
+ ```
130
+
131
+ - Show recent entries for a project:
132
+
133
+ ```bash
134
+ improve project show MyProject --last 10
135
+ ```
136
+
137
+ - Get recent feedback entries:
138
+
139
+ ```bash
140
+ improve project feedback MyProject --last 5
141
+ ```
142
+
143
+ - Export all tracking CSVs to `backup/`:
144
+
145
+ ```bash
146
+ improve export
147
+ ```
148
+
149
+ **Storage & File Layout**
150
+ - Tracking CSVs and user info are stored in the `tracking/` directory. Example files:
151
+ - `tracking/MyProject.csv` — per-project CSV with header `Day,Date,Progress,Productivity Level,Feedback`
152
+ - `tracking/user_info.json` — stores a small JSON object with `user_exists`, `user_name`, and `password`.
153
+
154
+ **Notes & Behavior**
155
+ - The CLI is implemented in `improve.py` and delegates commands to modules in `src/`.
156
+ - Password validation enforces a minimum length and requires digits, alphabetic, uppercase, lowercase and a special character (see `src/setup.py`).
157
+ - The `add-project` command is retained for backward compatibility but `project create` is preferred.
158
+
159
+ **Development & Contribution**
160
+ - Use `pip install -e .` and run `improve`, or run `python improve.py` without installing.
161
+ - Rebuild distributables with `python -m build` after packaging changes.
162
+ - The codebase is small and intended as a learning/demo project. Contributions or issues can be opened against the original upstream repository referenced in the code comments.
163
+
164
+
@@ -0,0 +1,13 @@
1
+ improve.py,sha256=URtBOBz0DZdxPUha_9X9KyOUSvjFSd8FRetnlx6unqk,4976
2
+ improve_cli-0.1.0.dist-info/licenses/LICENSE,sha256=Gx6gQSILjb0n_mNNc0n_t9PepS9513UsjlMLRojgF3E,1075
3
+ src/__init__.py,sha256=QQ453nXqN0jo3CcLjOviklQO-appJP-FCgo7Vm6wVp0,97
4
+ src/export.py,sha256=Tc7ZcJLjC4PN0E1eVt3mZeCGYNf11DmdlFq4NAmgAGs,886
5
+ src/help.py,sha256=gAzemecKifrDiNzm0TPCgWz9Xxr7Po48IE0nvlqm_Do,719
6
+ src/helpers.py,sha256=kILSXx7-1qCYidiqhDvVMvslL3AgECavavLHqkD0rlI,523
7
+ src/project.py,sha256=3KZBLPphUvvHjEWkKf7_HMRG0m9oFomb8H0xkKsm-3Y,3350
8
+ src/setup.py,sha256=B_pg9njQsCouq9LUr01el1RjNJH33lPxuqst24p3v0c,5640
9
+ improve_cli-0.1.0.dist-info/METADATA,sha256=HMcnNQgHD6sDPWkHqPbPG9oseYDkxoNwzbiHokiENQE,4836
10
+ improve_cli-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
+ improve_cli-0.1.0.dist-info/entry_points.txt,sha256=szXk0Yhe_HCKwjlJtqiPask90N0uhGlDhs1DBmzZSXU,41
12
+ improve_cli-0.1.0.dist-info/top_level.txt,sha256=1evx0noKIWBgNokQL2NSKjYonTD91I9OlfOYkJkc6x8,12
13
+ improve_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ improve = improve:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 harshit-malik25813
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ improve
2
+ src
src/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Initialising the directory to allow for importing functions from the files in the directory"""
src/export.py ADDED
@@ -0,0 +1,23 @@
1
+ import shutil
2
+ from pathlib import Path
3
+
4
+ # To export user tracking data to a CSV file
5
+ def export_data():
6
+ tracking_folder = Path("tracking")
7
+ if not tracking_folder.exists(): # If user exports data but the folder doesnt contain any
8
+ print("No tracking data found.")
9
+ return
10
+
11
+ csv_files = list(tracking_folder.glob("*.csv")) # Creating a list of files in the folder
12
+
13
+ destination_folder = Path("backup") # Creating a folder called backup to copy the tracked CSV files
14
+ destination_folder.mkdir(parents=True, exist_ok=True)
15
+
16
+ for csv_file in csv_files:
17
+ shutil.copy2(csv_file, destination_folder / csv_file.name)
18
+
19
+ print("Export was successful!")
20
+ print("Find your exported progress in CSV format in:\n" \
21
+ f"{destination_folder.resolve()}")
22
+ print("You can still continue with your progress as it hasnt been deleted")
23
+ return
src/help.py ADDED
@@ -0,0 +1,19 @@
1
+ def show_help():
2
+ print("Improve")
3
+ print("You might have entered a wrong command to get here")
4
+ print("To see the list of commands, run: improve help --commands")
5
+
6
+
7
+ def commands():
8
+ print("Available commands:")
9
+ print(" setup login")
10
+ print(" setup logout")
11
+ print(" setup update --username | --password")
12
+ print(" project list")
13
+ print(" project create <name>")
14
+ print(" project add-entry <name> [--day] [--date] [--progress] [--productivity] [--feedback]")
15
+ print(" project show <name> [--last N]")
16
+ print(" project feedback <name> [--last N]")
17
+ print(" project delete <name>")
18
+ print(" add-project <name> (legacy; prefer project create)")
19
+ print(" export")
src/helpers.py ADDED
@@ -0,0 +1,8 @@
1
+ def first_time(): # When user opens the program for the first time
2
+ print("Welcome to Improve!")
3
+ print("Improve is a CLI-tool designed to help people manage and organize their \n " \
4
+ "ongoing projects and track their progress in them")
5
+ print("This project has been developed and open-sourced by harshit-malik25813 on GitHub")
6
+ print("For all those nerds, find this project on https://github.com/harshit-malik25813/Improve")
7
+ print("To start, setup your account by running 'improve setup login' in your terminal")
8
+ return
src/project.py ADDED
@@ -0,0 +1,94 @@
1
+ import csv
2
+ from pathlib import Path
3
+ from typing import List
4
+ # Project functions, most important part of the program
5
+ # Defining constants
6
+ TRACKING = Path("tracking")
7
+ HEADER = ["Day", "Date", "Progress", "Productivity Level", "Feedback"]
8
+
9
+ # Function to make the project file
10
+ def _project_file(name: str) -> Path:
11
+ TRACKING.mkdir(parents=True, exist_ok=True)
12
+ return TRACKING / f"{name}.csv"
13
+ # Function to list the projects the user has created thus far
14
+
15
+ def list_projects() -> List[str]:
16
+ if not TRACKING.exists():
17
+ print("No projects found.")
18
+ return []
19
+ projects = [p.stem for p in TRACKING.glob("*.csv")]
20
+ if not projects:
21
+ print("No projects found.")
22
+ return []
23
+ print("Projects:")
24
+ for p in projects:
25
+ print(f" - {p}")
26
+ return projects
27
+ # Function to create a project
28
+
29
+ def create_project(name: str) -> Path:
30
+ # Path to project file
31
+ path = _project_file(name)
32
+ # If the user has already created a project with this name before
33
+ if path.exists():
34
+ print(f"Project '{name}' already exists at {path}")
35
+ return path
36
+ # Create the basic structure of the project in the
37
+ with path.open("w", newline="", encoding="utf-8") as f:
38
+ writer = csv.writer(f)
39
+ writer.writerow(HEADER)
40
+ print(f"Project '{name}' created at {path}")
41
+ return path
42
+ # Function to add an entry to the project
43
+
44
+ def add_entry(name: str, day: str = "", date: str = "", progress: str = "", productivity: str = "", feedback: str = ""):
45
+ path = _project_file(name)
46
+ if not path.exists():
47
+ print(f"Project '{name}' does not exist. Creating it now.")
48
+ create_project(name)
49
+ row = [day, date, progress, productivity, feedback]
50
+ with path.open("a", newline="", encoding="utf-8") as f:
51
+ writer = csv.writer(f)
52
+ writer.writerow(row)
53
+ print(f"Added entry to project '{name}'.")
54
+ # Function to retrieve feedbacks
55
+
56
+ def get_feedback(name: str, last: int = 5):
57
+ path = _project_file(name)
58
+ if not path.exists():
59
+ print(f"Project '{name}' not found.")
60
+ return
61
+ with path.open("r", encoding="utf-8") as f:
62
+ reader = list(csv.DictReader(f))
63
+ feedbacks = [r.get("Feedback", "") for r in reader if r.get("Feedback")]
64
+ if not feedbacks:
65
+ print("No feedback entries found.")
66
+ return
67
+ print(f"Last {last} feedback entries for '{name}':")
68
+ for fb in feedbacks[-last:]:
69
+ print(f" - {fb}")
70
+ # Function to show the project progress as the user asks
71
+
72
+ def show_project(name: str, last: int = 10):
73
+ path = _project_file(name)
74
+ if not path.exists():
75
+ print(f"Project '{name}' not found.")
76
+ return
77
+ with path.open("r", encoding="utf-8") as f:
78
+ reader = list(csv.DictReader(f))
79
+ if not reader:
80
+ print("No entries yet.")
81
+ return
82
+ rows = reader[-last:]
83
+ print(f"Showing last {len(rows)} entries for '{name}':")
84
+ for r in rows:
85
+ print(f"Day: {r.get('Day','')}, Date: {r.get('Date','')}, Progress: {r.get('Progress','')}, Productivity: {r.get('Productivity Level','')}, Feedback: {r.get('Feedback','')}")
86
+
87
+ # Function to delete the project
88
+ def delete_project(name: str):
89
+ path = _project_file(name)
90
+ if not path.exists():
91
+ print(f"Project '{name}' not found.")
92
+ return
93
+ path.unlink()
94
+ print(f"Project '{name}' deleted.")
src/setup.py ADDED
@@ -0,0 +1,152 @@
1
+ """User setup utilities for Improve CLI"""
2
+ from dataclasses import dataclass # to create dataclass for user
3
+ import json # to access user data in JSON file
4
+ from pathlib import Path # To manipulate the folder and files
5
+ import shutil # To make directories
6
+ from .export import export_data # Export data
7
+ from .help import show_help # Printing help
8
+
9
+ USER_FILE = Path("tracking") / "user_info.json" # The path of user file
10
+ # Declaring essential functions
11
+
12
+ def _load_user_info():
13
+ # Do not create the file by default. Return defaults if file missing.
14
+ if not USER_FILE.exists():
15
+ return {"user_exists": False, "user_name": "", "password": ""}
16
+ with USER_FILE.open("r", encoding="utf-8") as f:
17
+ return json.load(f)
18
+
19
+ # To write user_info
20
+ def _write_user_info(data: dict):
21
+ # To write user info in a JSON file for the first time
22
+ USER_FILE.parent.mkdir(parents=True, exist_ok=True)
23
+ with USER_FILE.open("w", encoding="utf-8") as f:
24
+ json.dump(data, f, indent=2)
25
+
26
+ # To validate password
27
+ def _validate_password(pw: str) -> tuple[bool, str]:
28
+ if len(pw) < 8:
29
+ return False, "Password must be at least 8 characters"
30
+ if not any(c.isdigit() for c in pw):
31
+ return False, "Password must contain a digit"
32
+ special_char = "!@#$%^&*()"
33
+ if not any(c in special_char for c in pw):
34
+ return False, "Password must contain a special character"
35
+ if not any(c.isalpha() for c in pw):
36
+ return False, "Password must contain alphabetic characters"
37
+ if not any(c.islower() for c in pw):
38
+ return False, "Password must contain a lowercase character"
39
+ if not any(c.isupper() for c in pw):
40
+ return False, "Password must contain an uppercase character"
41
+ return True, ""
42
+
43
+ # Logging user in
44
+ def login():
45
+ data = _load_user_info()
46
+ # If user exists, no need to do anything
47
+ if data.get("user_exists"):
48
+ print(f"A user already exists. You are logged in as {data.get('user_name')}")
49
+ return
50
+
51
+ print("User Account not set")
52
+ # Get user confirmation
53
+ ans = input("Do you want to set a user? (y/n): ").strip().lower()
54
+ if ans not in ("y", "n"):
55
+ print("Invalid input. Please enter y or n.")
56
+ return
57
+ if ans == "n":
58
+ print("Aborting user setup")
59
+ return
60
+ # Setting up a dataclass to help with easily accessing username and password
61
+ @dataclass
62
+ class User:
63
+ user_name: str
64
+ password: str
65
+ # Setting up the user
66
+ # Only let user leave if he enters the username and password correctly
67
+ while True:
68
+ # Validating username and password
69
+ user_name = input("Username: ").strip()
70
+ if len(user_name) < 4:
71
+
72
+ print("The length of user name should be at least 4 characters!")
73
+ continue
74
+
75
+ password = input("Password: ")
76
+ valid, msg = _validate_password(password)
77
+ if not valid:
78
+ print(msg)
79
+ continue
80
+ user = User(user_name, password)
81
+ break
82
+
83
+ new_data = {"user_exists": True, "user_name": user.user_name, "password": user.password}
84
+ _write_user_info(new_data)
85
+ print("User has been set successfully!")
86
+ print("Log in to start your improvement journey!")
87
+
88
+ # Logging user out
89
+ def logout():
90
+ data = _load_user_info()
91
+ # If user tries to log out but user doesn't exist
92
+ if not data.get("user_exists"):
93
+ print("No user is currently logged in.")
94
+ return
95
+ print("Logout")
96
+ # Asking user for confirmation
97
+ confirmation = input("Are you sure you want to logout? Your data will be deleted unless exported (y/n): ").strip().lower()
98
+ if confirmation not in ("y", "n"):
99
+ print("Please respond with y or n")
100
+ return
101
+ if confirmation == "n":
102
+ return
103
+ # Ask user if he wishes to export his data
104
+ ans = input("Do you wish to export your progress report to CSV before logout? (y/n): ").strip().lower()
105
+ if ans == "y":
106
+ export_data()
107
+ # After successful export, offer to delete the tracking directory
108
+ try:
109
+ tracking_dir = USER_FILE.parent
110
+ if tracking_dir.exists():
111
+ delete_confirm = input("Export complete. Do you want to delete the tracking directory? (y/n): ").strip().lower()
112
+ if delete_confirm == "y":
113
+ shutil.rmtree(tracking_dir)
114
+ print("Tracking directory deleted.")
115
+ except Exception as e:
116
+ print(f"Warning: failed to delete tracking directory: {e}")
117
+ # Update JSON file
118
+ no_user = {"user_exists": False, "user_name": "", "password": ""}
119
+ _write_user_info(no_user)
120
+ print("Logged out and user data cleared.")
121
+
122
+ # To update user data
123
+ def update_user(field: str):
124
+ if field not in ("username", "password"):
125
+ print("Invalid input!")
126
+ show_help()
127
+ return
128
+ data = _load_user_info()
129
+ if not data.get("user_exists"):
130
+ print("No user set. Run 'improve setup login' to create an account.")
131
+ return
132
+
133
+ if field == "username":
134
+ new_username = input("Enter new username: ").strip()
135
+ if len(new_username) < 4:
136
+ print("Username must be at least 4 characters long")
137
+ return
138
+ data["user_name"] = new_username
139
+ _write_user_info(data)
140
+ print("Username updated.")
141
+ return
142
+
143
+ if field == "password":
144
+ new_password = input("Enter new password: ")
145
+ valid, msg = _validate_password(new_password)
146
+ if not valid:
147
+ print(msg)
148
+ return
149
+ data["password"] = new_password
150
+ _write_user_info(data)
151
+ print("Password updated.")
152
+ return