interviewprep-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- interviewprep_cli-0.1.0/LICENSE +0 -0
- interviewprep_cli-0.1.0/PKG-INFO +257 -0
- interviewprep_cli-0.1.0/README.md +226 -0
- interviewprep_cli-0.1.0/pyproject.toml +60 -0
- interviewprep_cli-0.1.0/setup.cfg +4 -0
- interviewprep_cli-0.1.0/src/interviewprep/__init__.py +0 -0
- interviewprep_cli-0.1.0/src/interviewprep/cli.py +29 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/__init__.py +0 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/add.py +20 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/dashboard.py +81 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/delete.py +24 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/edit.py +22 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/export.py +18 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/import_problems.py +34 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/list.py +34 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/review.py +28 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/today.py +102 -0
- interviewprep_cli-0.1.0/src/interviewprep/commands/view.py +25 -0
- interviewprep_cli-0.1.0/src/interviewprep/models.py +32 -0
- interviewprep_cli-0.1.0/src/interviewprep/prompts.py +77 -0
- interviewprep_cli-0.1.0/src/interviewprep/scheduler.py +22 -0
- interviewprep_cli-0.1.0/src/interviewprep/storage.py +137 -0
- interviewprep_cli-0.1.0/src/interviewprep/streak.py +32 -0
- interviewprep_cli-0.1.0/src/interviewprep_cli.egg-info/PKG-INFO +257 -0
- interviewprep_cli-0.1.0/src/interviewprep_cli.egg-info/SOURCES.txt +32 -0
- interviewprep_cli-0.1.0/src/interviewprep_cli.egg-info/dependency_links.txt +1 -0
- interviewprep_cli-0.1.0/src/interviewprep_cli.egg-info/entry_points.txt +2 -0
- interviewprep_cli-0.1.0/src/interviewprep_cli.egg-info/requires.txt +9 -0
- interviewprep_cli-0.1.0/src/interviewprep_cli.egg-info/top_level.txt +1 -0
- interviewprep_cli-0.1.0/tests/test_basic.py +4 -0
- interviewprep_cli-0.1.0/tests/test_command.py +440 -0
- interviewprep_cli-0.1.0/tests/test_model.py +74 -0
- interviewprep_cli-0.1.0/tests/test_scheduler.py +82 -0
- interviewprep_cli-0.1.0/tests/test_storage.py +73 -0
|
File without changes
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: interviewprep-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool for organizing technical interview preparation
|
|
5
|
+
Author-email: Tevita Mathias <mathias.tevita@icloud.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/aldohl95/InterviewPrepCli
|
|
8
|
+
Project-URL: Repository, https://github.com/aldohl95/InterviewPrepCli
|
|
9
|
+
Project-URL: Issues, https://github.com/aldohl95/InterviewPrepCli/issues
|
|
10
|
+
Keywords: cli,leetcode,interview-prep,spaced-repetition
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Education
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: platformdirs
|
|
23
|
+
Requires-Dist: click
|
|
24
|
+
Requires-Dist: rich
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
28
|
+
Requires-Dist: black>=24.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# InterviewPrep CLI
|
|
33
|
+
|
|
34
|
+
A command-line application for organizing and optimizing technical interview preparation. Instead of tracking LeetCode problems, study notes, and review schedules across spreadsheets and multiple apps, everything lives in a single terminal-based tool with spaced repetition built in.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Problem Tracker** — full CRUD for practice attempts with problem name, pattern, difficulty, time taken, and self-assessment fields
|
|
39
|
+
- **Spaced Repetition Engine** — automatic scheduling of review dates based on personal difficulty and whether hints were used
|
|
40
|
+
- **Progress Dashboard** — aggregated statistics on problems solved, patterns practiced, and difficulty distribution
|
|
41
|
+
- **Daily Study Planner** — shows what's due for review today with streak tracking
|
|
42
|
+
- **Data Import / Export** — JSON-based backup and restore with merge and replace modes
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Requires Python 3.11 or higher.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/aldohl95/InterviewPrepCli.git
|
|
50
|
+
cd InterviewPrepCli
|
|
51
|
+
python3 -m venv .venv
|
|
52
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
53
|
+
pip install -e ".[dev]" --no-build-isolation
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Verify the installation:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
interviewprep --help
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### Adding a problem
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
interviewprep add
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Walks through interactive prompts for problem name, pattern (e.g. "hash map"), LeetCode difficulty, personal difficulty (1–10), time taken, whether you needed help, and an optional recognition sentence.
|
|
71
|
+
|
|
72
|
+
### Listing all problems
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
interviewprep list
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Displays a summary table with problem number, name, date attempted, personal difficulty, and whether it needs resolving.
|
|
79
|
+
|
|
80
|
+
### Viewing full details
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
interviewprep view 1
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Shows every field for the numbered problem.
|
|
87
|
+
|
|
88
|
+
### Editing a problem
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
interviewprep edit 1
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Prompts field-by-field with current values as defaults. Press Enter to keep any field unchanged.
|
|
95
|
+
|
|
96
|
+
### Deleting a problem
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
interviewprep delete 1
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Confirmation prompt before deletion.
|
|
103
|
+
|
|
104
|
+
### Logging a new review
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
interviewprep review 1
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Creates a new attempt record for an existing problem and calculates the next review date based on the spaced repetition algorithm.
|
|
111
|
+
|
|
112
|
+
### Progress dashboard
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
interviewprep dashboard
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Displays statistics: total problems solved, breakdown by LeetCode difficulty, top patterns practiced, and problems still needing resolve.
|
|
119
|
+
|
|
120
|
+
### Daily study planner
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
interviewprep today
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Shows problems due for review today, falls back to upcoming reviews if nothing is due, and tracks your daily streak.
|
|
127
|
+
|
|
128
|
+
### Exporting data
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
interviewprep export --output=backup.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Writes all problems to a JSON file for backup.
|
|
135
|
+
|
|
136
|
+
### Importing data
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
interviewprep import --input=backup.json --mode=merge
|
|
140
|
+
interviewprep import --input=backup.json --mode=replace
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Merge mode adds imported problems to existing data. Replace mode overwrites everything.
|
|
144
|
+
|
|
145
|
+
## Architecture
|
|
146
|
+
|
|
147
|
+
### Directory structure
|
|
148
|
+
### Full project structure
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
interviewprep-cli/
|
|
152
|
+
├── src/
|
|
153
|
+
│ └── interviewprep/
|
|
154
|
+
│ ├── __init__.py
|
|
155
|
+
│ ├── cli.py — Click entry point and command registration
|
|
156
|
+
│ ├── models.py — Problem dataclass and LeetcodeDifficulty enum
|
|
157
|
+
│ ├── storage.py — JSON persistence via platformdirs
|
|
158
|
+
│ ├── prompts.py — shared interactive prompt logic
|
|
159
|
+
│ ├── scheduler.py — spaced repetition algorithm
|
|
160
|
+
│ └── commands/ — one file per CLI command
|
|
161
|
+
│ ├── __init__.py
|
|
162
|
+
│ ├── add.py
|
|
163
|
+
│ ├── list.py
|
|
164
|
+
│ ├── view.py
|
|
165
|
+
│ ├── edit.py
|
|
166
|
+
│ ├── delete.py
|
|
167
|
+
│ ├── review.py
|
|
168
|
+
│ ├── dashboard.py
|
|
169
|
+
│ ├── today.py
|
|
170
|
+
│ ├── export.py
|
|
171
|
+
│ └── import_problems.py
|
|
172
|
+
│
|
|
173
|
+
├── tests/
|
|
174
|
+
│ ├── test_basic.py
|
|
175
|
+
│ ├── test_model.py
|
|
176
|
+
│ ├── test_storage.py
|
|
177
|
+
│ ├── test_scheduler.py
|
|
178
|
+
│ └── test_command.py
|
|
179
|
+
│
|
|
180
|
+
├── docs/
|
|
181
|
+
│ └── scheduler.md — algorithm design and tradeoffs
|
|
182
|
+
│
|
|
183
|
+
├── README.md
|
|
184
|
+
├── SPEC.md
|
|
185
|
+
├── LICENSE
|
|
186
|
+
└── pyproject.toml — project config, dependencies, tool settings
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Data storage
|
|
190
|
+
|
|
191
|
+
Problems are stored as JSON at the platform-appropriate user data directory (via `platformdirs`):
|
|
192
|
+
|
|
193
|
+
- **Linux**: `~/.local/share/interviewprep/problems.json`
|
|
194
|
+
- **macOS**: `~/Library/Application Support/interviewprep/problems.json`
|
|
195
|
+
- **Windows**: `%LOCALAPPDATA%\interviewprep\problems.json`
|
|
196
|
+
|
|
197
|
+
### Spaced repetition
|
|
198
|
+
|
|
199
|
+
A custom piecewise algorithm designed for interview preparation. First reviews use gentle intervals (1 or 3 days). Subsequent reviews scale from 1 day (needed help or difficulty ≥ 8) up to 30 days (mastered). Full details in `docs/scheduler.md`.
|
|
200
|
+
|
|
201
|
+
## Design Decisions
|
|
202
|
+
|
|
203
|
+
Several deliberate tradeoffs are documented in-code and worth calling out:
|
|
204
|
+
|
|
205
|
+
- **JSON over SQLite** — chosen for simplicity and human-readable data. SQLite migration is noted as future work.
|
|
206
|
+
- **Custom scheduling algorithm over SM-2** — the industry-standard SM-2 algorithm was overkill for the expected data size. The custom algorithm is simpler to test, defend, and reason about.
|
|
207
|
+
- **Problem name as identifier** — prior attempts are looked up by name rather than by a separate ID system. Acceptable tradeoff for a single-user tool.
|
|
208
|
+
- **New attempts as separate records** — the `review` command creates new records rather than mutating existing ones, preserving history.
|
|
209
|
+
- **Edit does not recalculate schedules** — editing a problem is treated as metadata correction. Users who want to update their schedule should use `review` instead.
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
### Running tests
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pytest
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Current test count: **42 tests passing** across models, storage, scheduler, and all commands.
|
|
220
|
+
|
|
221
|
+
### Formatting and linting
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
black src/ tests/
|
|
225
|
+
ruff check src/ tests/
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Configuration lives in `pyproject.toml`.
|
|
229
|
+
|
|
230
|
+
### Test coverage areas
|
|
231
|
+
|
|
232
|
+
- Data model validation (boundary values, invalid inputs)
|
|
233
|
+
- Storage round-trip (save then load produces equivalent data)
|
|
234
|
+
- Scheduler algorithm (every branch and boundary tested)
|
|
235
|
+
- Every CLI command (success paths and error cases)
|
|
236
|
+
|
|
237
|
+
## Future Work
|
|
238
|
+
|
|
239
|
+
- Migrate storage layer from JSON to SQLite for efficient querying
|
|
240
|
+
- Add CSV import/export format
|
|
241
|
+
- Refactor to split `Problem` (identity) from `Attempt` (per-session data)
|
|
242
|
+
- Recalculate `next_review_date` on edit when scheduling fields change
|
|
243
|
+
- Add filtering to `list` command (by pattern, difficulty, date range)
|
|
244
|
+
- CI pipeline with GitHub Actions
|
|
245
|
+
|
|
246
|
+
## Tech Stack
|
|
247
|
+
|
|
248
|
+
- **Python 3.11+**
|
|
249
|
+
- **Click** — CLI framework
|
|
250
|
+
- **Rich** — formatted terminal output
|
|
251
|
+
- **platformdirs** — cross-platform data directory handling
|
|
252
|
+
- **pytest** — testing framework
|
|
253
|
+
- **Black** and **Ruff** — code formatting and linting
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# InterviewPrep CLI
|
|
2
|
+
|
|
3
|
+
A command-line application for organizing and optimizing technical interview preparation. Instead of tracking LeetCode problems, study notes, and review schedules across spreadsheets and multiple apps, everything lives in a single terminal-based tool with spaced repetition built in.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Problem Tracker** — full CRUD for practice attempts with problem name, pattern, difficulty, time taken, and self-assessment fields
|
|
8
|
+
- **Spaced Repetition Engine** — automatic scheduling of review dates based on personal difficulty and whether hints were used
|
|
9
|
+
- **Progress Dashboard** — aggregated statistics on problems solved, patterns practiced, and difficulty distribution
|
|
10
|
+
- **Daily Study Planner** — shows what's due for review today with streak tracking
|
|
11
|
+
- **Data Import / Export** — JSON-based backup and restore with merge and replace modes
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Requires Python 3.11 or higher.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git clone https://github.com/aldohl95/InterviewPrepCli.git
|
|
19
|
+
cd InterviewPrepCli
|
|
20
|
+
python3 -m venv .venv
|
|
21
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
22
|
+
pip install -e ".[dev]" --no-build-isolation
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Verify the installation:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
interviewprep --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Adding a problem
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
interviewprep add
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Walks through interactive prompts for problem name, pattern (e.g. "hash map"), LeetCode difficulty, personal difficulty (1–10), time taken, whether you needed help, and an optional recognition sentence.
|
|
40
|
+
|
|
41
|
+
### Listing all problems
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
interviewprep list
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Displays a summary table with problem number, name, date attempted, personal difficulty, and whether it needs resolving.
|
|
48
|
+
|
|
49
|
+
### Viewing full details
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
interviewprep view 1
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Shows every field for the numbered problem.
|
|
56
|
+
|
|
57
|
+
### Editing a problem
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
interviewprep edit 1
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Prompts field-by-field with current values as defaults. Press Enter to keep any field unchanged.
|
|
64
|
+
|
|
65
|
+
### Deleting a problem
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
interviewprep delete 1
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Confirmation prompt before deletion.
|
|
72
|
+
|
|
73
|
+
### Logging a new review
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
interviewprep review 1
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Creates a new attempt record for an existing problem and calculates the next review date based on the spaced repetition algorithm.
|
|
80
|
+
|
|
81
|
+
### Progress dashboard
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
interviewprep dashboard
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Displays statistics: total problems solved, breakdown by LeetCode difficulty, top patterns practiced, and problems still needing resolve.
|
|
88
|
+
|
|
89
|
+
### Daily study planner
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
interviewprep today
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Shows problems due for review today, falls back to upcoming reviews if nothing is due, and tracks your daily streak.
|
|
96
|
+
|
|
97
|
+
### Exporting data
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
interviewprep export --output=backup.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Writes all problems to a JSON file for backup.
|
|
104
|
+
|
|
105
|
+
### Importing data
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
interviewprep import --input=backup.json --mode=merge
|
|
109
|
+
interviewprep import --input=backup.json --mode=replace
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Merge mode adds imported problems to existing data. Replace mode overwrites everything.
|
|
113
|
+
|
|
114
|
+
## Architecture
|
|
115
|
+
|
|
116
|
+
### Directory structure
|
|
117
|
+
### Full project structure
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
interviewprep-cli/
|
|
121
|
+
├── src/
|
|
122
|
+
│ └── interviewprep/
|
|
123
|
+
│ ├── __init__.py
|
|
124
|
+
│ ├── cli.py — Click entry point and command registration
|
|
125
|
+
│ ├── models.py — Problem dataclass and LeetcodeDifficulty enum
|
|
126
|
+
│ ├── storage.py — JSON persistence via platformdirs
|
|
127
|
+
│ ├── prompts.py — shared interactive prompt logic
|
|
128
|
+
│ ├── scheduler.py — spaced repetition algorithm
|
|
129
|
+
│ └── commands/ — one file per CLI command
|
|
130
|
+
│ ├── __init__.py
|
|
131
|
+
│ ├── add.py
|
|
132
|
+
│ ├── list.py
|
|
133
|
+
│ ├── view.py
|
|
134
|
+
│ ├── edit.py
|
|
135
|
+
│ ├── delete.py
|
|
136
|
+
│ ├── review.py
|
|
137
|
+
│ ├── dashboard.py
|
|
138
|
+
│ ├── today.py
|
|
139
|
+
│ ├── export.py
|
|
140
|
+
│ └── import_problems.py
|
|
141
|
+
│
|
|
142
|
+
├── tests/
|
|
143
|
+
│ ├── test_basic.py
|
|
144
|
+
│ ├── test_model.py
|
|
145
|
+
│ ├── test_storage.py
|
|
146
|
+
│ ├── test_scheduler.py
|
|
147
|
+
│ └── test_command.py
|
|
148
|
+
│
|
|
149
|
+
├── docs/
|
|
150
|
+
│ └── scheduler.md — algorithm design and tradeoffs
|
|
151
|
+
│
|
|
152
|
+
├── README.md
|
|
153
|
+
├── SPEC.md
|
|
154
|
+
├── LICENSE
|
|
155
|
+
└── pyproject.toml — project config, dependencies, tool settings
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Data storage
|
|
159
|
+
|
|
160
|
+
Problems are stored as JSON at the platform-appropriate user data directory (via `platformdirs`):
|
|
161
|
+
|
|
162
|
+
- **Linux**: `~/.local/share/interviewprep/problems.json`
|
|
163
|
+
- **macOS**: `~/Library/Application Support/interviewprep/problems.json`
|
|
164
|
+
- **Windows**: `%LOCALAPPDATA%\interviewprep\problems.json`
|
|
165
|
+
|
|
166
|
+
### Spaced repetition
|
|
167
|
+
|
|
168
|
+
A custom piecewise algorithm designed for interview preparation. First reviews use gentle intervals (1 or 3 days). Subsequent reviews scale from 1 day (needed help or difficulty ≥ 8) up to 30 days (mastered). Full details in `docs/scheduler.md`.
|
|
169
|
+
|
|
170
|
+
## Design Decisions
|
|
171
|
+
|
|
172
|
+
Several deliberate tradeoffs are documented in-code and worth calling out:
|
|
173
|
+
|
|
174
|
+
- **JSON over SQLite** — chosen for simplicity and human-readable data. SQLite migration is noted as future work.
|
|
175
|
+
- **Custom scheduling algorithm over SM-2** — the industry-standard SM-2 algorithm was overkill for the expected data size. The custom algorithm is simpler to test, defend, and reason about.
|
|
176
|
+
- **Problem name as identifier** — prior attempts are looked up by name rather than by a separate ID system. Acceptable tradeoff for a single-user tool.
|
|
177
|
+
- **New attempts as separate records** — the `review` command creates new records rather than mutating existing ones, preserving history.
|
|
178
|
+
- **Edit does not recalculate schedules** — editing a problem is treated as metadata correction. Users who want to update their schedule should use `review` instead.
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
### Running tests
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pytest
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Current test count: **42 tests passing** across models, storage, scheduler, and all commands.
|
|
189
|
+
|
|
190
|
+
### Formatting and linting
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
black src/ tests/
|
|
194
|
+
ruff check src/ tests/
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Configuration lives in `pyproject.toml`.
|
|
198
|
+
|
|
199
|
+
### Test coverage areas
|
|
200
|
+
|
|
201
|
+
- Data model validation (boundary values, invalid inputs)
|
|
202
|
+
- Storage round-trip (save then load produces equivalent data)
|
|
203
|
+
- Scheduler algorithm (every branch and boundary tested)
|
|
204
|
+
- Every CLI command (success paths and error cases)
|
|
205
|
+
|
|
206
|
+
## Future Work
|
|
207
|
+
|
|
208
|
+
- Migrate storage layer from JSON to SQLite for efficient querying
|
|
209
|
+
- Add CSV import/export format
|
|
210
|
+
- Refactor to split `Problem` (identity) from `Attempt` (per-session data)
|
|
211
|
+
- Recalculate `next_review_date` on edit when scheduling fields change
|
|
212
|
+
- Add filtering to `list` command (by pattern, difficulty, date range)
|
|
213
|
+
- CI pipeline with GitHub Actions
|
|
214
|
+
|
|
215
|
+
## Tech Stack
|
|
216
|
+
|
|
217
|
+
- **Python 3.11+**
|
|
218
|
+
- **Click** — CLI framework
|
|
219
|
+
- **Rich** — formatted terminal output
|
|
220
|
+
- **platformdirs** — cross-platform data directory handling
|
|
221
|
+
- **pytest** — testing framework
|
|
222
|
+
- **Black** and **Ruff** — code formatting and linting
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "interviewprep-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A CLI tool for organizing technical interview preparation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Tevita Mathias", email = "mathias.tevita@icloud.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["cli", "leetcode", "interview-prep", "spaced-repetition"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Education",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
dependencies = ["platformdirs", "click", "rich"]
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/aldohl95/InterviewPrepCli"
|
|
31
|
+
Repository = "https://github.com/aldohl95/InterviewPrepCli"
|
|
32
|
+
Issues = "https://github.com/aldohl95/InterviewPrepCli/issues"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7.4",
|
|
37
|
+
"pytest-cov>=4.1",
|
|
38
|
+
"black >= 24.0",
|
|
39
|
+
"ruff >= 0.4",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
interviewprep = "interviewprep.cli:main"
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
testpaths = ["tests"]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
[tool.black]
|
|
52
|
+
target-version = ["py311"]
|
|
53
|
+
line-length = 88
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 88
|
|
57
|
+
target-version="py311"
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "F", "W"]
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from interviewprep.commands.add import add
|
|
3
|
+
from interviewprep.commands.list import list_problems
|
|
4
|
+
from interviewprep.commands.view import view
|
|
5
|
+
from interviewprep.commands.edit import edit
|
|
6
|
+
from interviewprep.commands.delete import delete
|
|
7
|
+
from interviewprep.commands.review import review
|
|
8
|
+
from interviewprep.commands.dashboard import dashboard
|
|
9
|
+
from interviewprep.commands.today import today
|
|
10
|
+
from interviewprep.commands.export import export
|
|
11
|
+
from interviewprep.commands.import_problems import import_problems
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group()
|
|
15
|
+
def main():
|
|
16
|
+
"""Interview CLI - manage your technical interview preparation."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
main.add_command(add)
|
|
21
|
+
main.add_command(list_problems)
|
|
22
|
+
main.add_command(view)
|
|
23
|
+
main.add_command(edit)
|
|
24
|
+
main.add_command(delete)
|
|
25
|
+
main.add_command(review)
|
|
26
|
+
main.add_command(dashboard)
|
|
27
|
+
main.add_command(today)
|
|
28
|
+
main.add_command(export)
|
|
29
|
+
main.add_command(import_problems)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from interviewprep.storage import save_problems, load_problems, find_attempts_by_name
|
|
3
|
+
from interviewprep.prompts import prompt_for_problem
|
|
4
|
+
from interviewprep.scheduler import calculate_next_review_date
|
|
5
|
+
from interviewprep.streak import update_streak
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command()
|
|
9
|
+
def add():
|
|
10
|
+
click.echo("===Add New Problem===")
|
|
11
|
+
|
|
12
|
+
problem = prompt_for_problem()
|
|
13
|
+
problems = load_problems()
|
|
14
|
+
prior = find_attempts_by_name(problems, problem.problem_name)
|
|
15
|
+
problem.next_review_date = calculate_next_review_date(problem, prior)
|
|
16
|
+
problems.append(problem)
|
|
17
|
+
save_problems(problems)
|
|
18
|
+
update_streak()
|
|
19
|
+
|
|
20
|
+
click.echo(f"\nSaved '{problem.problem_name}' saved successfully")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from interviewprep.storage import load_problems, find_attempts_by_needs_resolve
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
import heapq
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command()
|
|
9
|
+
def dashboard():
|
|
10
|
+
problems = load_problems()
|
|
11
|
+
console = Console()
|
|
12
|
+
freq_table = Table()
|
|
13
|
+
average_difficulty_table = Table()
|
|
14
|
+
resolve_count_table = Table()
|
|
15
|
+
leetcode_difficulty_table = Table()
|
|
16
|
+
pattern_frequency = {}
|
|
17
|
+
difficulty_frequency = {}
|
|
18
|
+
personal_difficulty_frequency = {}
|
|
19
|
+
|
|
20
|
+
leetcode_difficulty_table.add_column("#")
|
|
21
|
+
leetcode_difficulty_table.add_column("Leetcode Difficulty")
|
|
22
|
+
leetcode_difficulty_table.add_column("Solved Count")
|
|
23
|
+
freq_table.add_column("#")
|
|
24
|
+
freq_table.add_column("Pattern")
|
|
25
|
+
freq_table.add_column("Solved Count")
|
|
26
|
+
average_difficulty_table.add_column("#")
|
|
27
|
+
average_difficulty_table.add_column("Pattern")
|
|
28
|
+
average_difficulty_table.add_column("Average Personal Difficulty")
|
|
29
|
+
resolve_count_table.add_column("Total Resolves Needed")
|
|
30
|
+
|
|
31
|
+
if not problems:
|
|
32
|
+
click.echo("No Saved Problems yet")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
# for Leetcode Difficulty
|
|
36
|
+
for problem in problems:
|
|
37
|
+
difficulty_frequency[problem.leetcode_difficulty.value] = (
|
|
38
|
+
1 + difficulty_frequency.get(problem.leetcode_difficulty.value, 0)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
for i, (difficulty, count) in enumerate(difficulty_frequency.items(), start=1):
|
|
42
|
+
leetcode_difficulty_table.add_row(str(i), difficulty, str(count))
|
|
43
|
+
|
|
44
|
+
# for patter frequency
|
|
45
|
+
for problem in problems:
|
|
46
|
+
pattern_frequency[problem.pattern] = 1 + pattern_frequency.get(
|
|
47
|
+
problem.pattern, 0
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
top_five_patterns = heapq.nlargest(
|
|
51
|
+
5, pattern_frequency.items(), key=lambda item: item[1]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
for i, (pattern, count) in enumerate(top_five_patterns, start=1):
|
|
55
|
+
freq_table.add_row(str(i), pattern, str(count))
|
|
56
|
+
|
|
57
|
+
# for average difficulty
|
|
58
|
+
for problem in problems:
|
|
59
|
+
pattern = problem.pattern
|
|
60
|
+
difficulty = problem.personal_difficulty
|
|
61
|
+
|
|
62
|
+
if pattern not in personal_difficulty_frequency:
|
|
63
|
+
personal_difficulty_frequency[pattern] = {"total_difficulty": 0, "count": 0}
|
|
64
|
+
|
|
65
|
+
personal_difficulty_frequency[pattern]["total_difficulty"] += difficulty
|
|
66
|
+
personal_difficulty_frequency[pattern]["count"] += 1
|
|
67
|
+
|
|
68
|
+
for i, (pattern, count) in enumerate(
|
|
69
|
+
personal_difficulty_frequency.items(), start=1
|
|
70
|
+
):
|
|
71
|
+
average = count["total_difficulty"] / count["count"]
|
|
72
|
+
average_difficulty_table.add_row(str(i), pattern, str(average))
|
|
73
|
+
|
|
74
|
+
# problems needing resolve
|
|
75
|
+
resolve_problems = find_attempts_by_needs_resolve(problems, True)
|
|
76
|
+
resolve_count_table.add_row(str(len(resolve_problems)))
|
|
77
|
+
|
|
78
|
+
console.print(leetcode_difficulty_table)
|
|
79
|
+
console.print(freq_table)
|
|
80
|
+
console.print(average_difficulty_table)
|
|
81
|
+
console.print(resolve_count_table)
|