Skip to content

Commit 81bbfc7

Browse files
authored
feat: re-architect repo (#10)
1 parent 238ec7b commit 81bbfc7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3968
-441
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env python3
2+
"""Reusable file comparison tool for template validation."""
3+
4+
import difflib
5+
from pathlib import Path
6+
7+
import typer
8+
9+
10+
def compare_files(file1: Path, file2: Path, label1: str, label2: str) -> bool:
11+
"""Compare two files and show differences. Returns True if identical."""
12+
typer.echo(f"\n{'='*60}")
13+
typer.echo(f"COMPARING: {file1.name}")
14+
typer.echo(f"{label1}: {file1}")
15+
typer.echo(f"{label2}: {file2}")
16+
typer.echo(f"{'='*60}")
17+
18+
if not file1.exists():
19+
typer.echo(f"❌ MISSING: {file1} does not exist")
20+
return False
21+
22+
if not file2.exists():
23+
typer.echo(f"❌ MISSING: {file2} does not exist")
24+
return False
25+
26+
content1 = file1.read_text().splitlines(keepends=True)
27+
content2 = file2.read_text().splitlines(keepends=True)
28+
29+
diff = list(
30+
difflib.unified_diff(
31+
content1,
32+
content2,
33+
fromfile=f"{label1}/{file1.name}",
34+
tofile=f"{label2}/{file2.name}",
35+
lineterm="",
36+
)
37+
)
38+
39+
if not diff:
40+
typer.echo("✅ FILES IDENTICAL")
41+
return True
42+
else:
43+
typer.echo("❌ DIFFERENCES FOUND:")
44+
for line in diff:
45+
typer.echo(line)
46+
return False
47+
48+
49+
def main(
50+
mode: str = typer.Argument(help="Compare template files or generated files (template|generated)"),
51+
problem: str = typer.Option("invert_binary_tree", help="Problem name for comparison"),
52+
):
53+
"""Compare files for template validation."""
54+
if mode not in ["template", "generated"]:
55+
typer.echo(f"❌ ERROR: Invalid mode '{mode}'. Use 'template' or 'generated'")
56+
return
57+
58+
base_dir = Path(__file__).parent.parent.parent
59+
60+
files_to_compare = ["solution.py", "tests.py", "README.md", "playground.ipynb", "__init__.py"]
61+
62+
if mode == "template":
63+
# Compare reference vs template source
64+
dir1 = base_dir / "leetcode" / ".example" / problem
65+
dir2 = base_dir / ".templates" / "leetcode" / ".example" / "{{cookiecutter.problem_name}}"
66+
label1, label2 = "Reference", "Template"
67+
typer.echo("TEMPLATE SOURCE ANALYSIS")
68+
69+
elif mode == "generated":
70+
# Compare reference vs currently generated
71+
dir1 = base_dir / "leetcode" / ".example" / problem
72+
dir2 = base_dir / "leetcode" / problem
73+
label1, label2 = "Reference", "Generated"
74+
typer.echo("GENERATED FILES VALIDATION")
75+
76+
if not dir2.exists():
77+
typer.echo(f"\n❌ ERROR: Generated directory does not exist: {dir2}")
78+
typer.echo(f"Run: make p-gen PROBLEM={problem}")
79+
return
80+
81+
typer.echo(f"{label1}: {dir1}")
82+
typer.echo(f"{label2}: {dir2}")
83+
84+
identical_count = 0
85+
for filename in files_to_compare:
86+
file1 = dir1 / filename
87+
file2 = dir2 / filename
88+
if compare_files(file1, file2, label1, label2):
89+
identical_count += 1
90+
91+
typer.echo(f"\n{'='*60}")
92+
typer.echo(f"SUMMARY: {identical_count}/{len(files_to_compare)} files identical")
93+
typer.echo("- ✅ = Files identical")
94+
typer.echo("- ❌ = Differences found or missing files")
95+
96+
97+
if __name__ == "__main__":
98+
typer.run(main)
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Cookiecutter Template Modernization Plan
2+
3+
## TASK PURPOSE & CRITICAL RULES
4+
5+
**PURPOSE:** Update the cookiecutter template to generate files that exactly match the reference structure in `.templates/leetcode/.example/{{cookiecutter.problem_name}}/`
6+
7+
**REFERENCE DIRECTORIES (NEVER MODIFY - THESE ARE EXAMPLES):**
8+
9+
- `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` - Shows what the template SHOULD generate
10+
- `leetcode/.example/` - Generated file examples for comparison
11+
12+
**ACTUAL TEMPLATE DIRECTORY (MODIFY THIS):**
13+
14+
- `.templates/leetcode/{{cookiecutter.problem_name}}/` - The cookiecutter template files to update
15+
16+
**WORKFLOW:**
17+
18+
1. Look at `.templates/leetcode/.example/{{cookiecutter.problem_name}}/` to see target structure
19+
2. Modify `.templates/leetcode/{{cookiecutter.problem_name}}/` to match the reference
20+
3. Generate with `make p-gen`
21+
4. Compare generated files vs reference with `make p-validate`
22+
23+
**ERROR PREVENTION:** The template directory does NOT have `.example` in the path!
24+
25+
## Analysis Summary
26+
27+
**Target Structure**: `leetcode/.example/` contains the reference implementation
28+
**Key Differences Found:**
29+
30+
- `leetcode/.example/` has `__init__.py` files (missing in old template)
31+
- `leetcode/.example/` uses modern Python syntax (`TreeNode | None` vs `Optional[TreeNode]`)
32+
- `leetcode/.example/` follows project's coding standards more closely
33+
- Template must generate files identical to `leetcode/.example/` structure
34+
35+
## Implementation Plan
36+
37+
### 0. Explicit File Content Analysis
38+
39+
- **Tool**: `.amazonq/plan/compare_template_files.py` (already exists - no need to implement)
40+
- **Usage**:
41+
- `poetry run python .amazonq/plan/compare_template_files.py generated --problem=PROBLEM_NAME` - Compare generated files vs reference
42+
- **Analysis**: Line-by-line diff of all file types
43+
- **Document**: Exact differences and required changes
44+
- **Verify**: Template variables handle all variations
45+
46+
### 1. Incremental Template Updates (File-by-File Approach)
47+
48+
#### Phase 1: Add `__init__.py`
49+
50+
- **Add**: Empty `__init__.py` file to template
51+
- **Validate**: `make p-gen``make p-validate``make lint`
52+
53+
#### Phase 2: Fix `solution.py`
54+
55+
- **Update**: Modern syntax (`TreeNode | None`), clean template logic
56+
- **Validate**: `make p-gen``make p-validate``make lint`
57+
58+
#### Phase 3: Fix `tests.py`
59+
60+
- **Update**: Relative imports (`from .solution`), clean structure
61+
- **Validate**: `make p-gen``make p-validate``make lint`
62+
63+
#### Phase 4: Fix `README.md`
64+
65+
- **Update**: Clean formatting, proper markdown
66+
- **Validate**: `make p-gen``make p-validate``make lint`
67+
68+
#### Phase 5: Fix `playground.ipynb`
69+
70+
- **Update**: Clean cells without execution state
71+
- **Validate**: `make p-gen``make p-validate``make lint`
72+
73+
**Benefits**: Isolated debugging, safer progression, easier rollback
74+
75+
### 2. Multi-Problem Type Testing
76+
77+
- **Test Cases**: Ensure template handles all problem types
78+
- Basic array: `two_sum` (return `list[int]`)
79+
- Tree: `invert_binary_tree` (return `TreeNode | None`)
80+
- String: problems returning `str`
81+
- Boolean: problems returning `bool`
82+
- No imports vs TreeNode/ListNode imports
83+
- **Validation**: Each type generates correctly
84+
85+
### 3. Modernize cookiecutter.json Schema
86+
87+
- **Base on**: Existing `.templates/leetcode/.example/examples/` JSON5 files
88+
- **Ensure**: All template variables are properly defined
89+
- **Add**: Missing fields found in real examples
90+
- **Note**: Variable mapping handled by `gen.py` `convert_arrays_to_nested()`
91+
92+
### 4. Update Template Files
93+
94+
#### solution.py
95+
96+
- Use modern type hints (`TreeNode | None` not `Optional[TreeNode]`)
97+
- Match exact import patterns from real examples
98+
- Ensure proper TODO placeholder format
99+
100+
#### tests.py
101+
102+
- Follow `@logged_test` decorator pattern
103+
- Use parametrized pytest structure
104+
- Match logging format from real examples
105+
106+
#### README.md
107+
108+
- Follow exact format from real examples
109+
- Include proper problem description formatting
110+
111+
#### playground.ipynb
112+
113+
- Ensure notebook structure matches real examples
114+
115+
### 5. Template Generation Logic
116+
117+
- **File**: `.templates/leetcode/gen.py` (already handles variable mapping)
118+
- **Integration**: Works with `make p-gen PROBLEM=name` (verified in Makefile)
119+
- **Update**: Handle new `__init__.py` file
120+
- **Process**: JSON → `gen.py` → cookiecutter → `leetcode/$(PROBLEM)/`
121+
122+
### 6. Automated Validation System
123+
124+
- **Tool**: Reusable `.amazonq/plan/compare_template_files.py`
125+
- **Usage**:
126+
```bash
127+
# Validate current template generates correct files
128+
poetry run python .amazonq/plan/compare_template_files.py generated --problem=invert_binary_tree
129+
```
130+
- **Makefile**: `make p-validate PROBLEM=name` (implemented)
131+
- **Test**: Template regression testing
132+
- **Ensure**: `make p-gen` + `make lint` + `make p-test` all pass
133+
134+
### 7. Testing & Validation
135+
136+
- **Test**: Template generation with existing JSON files
137+
- **Verify**: Generated files match `leetcode/.example/` structure exactly
138+
- **Compare**: Automated diff against reference files
139+
- **Ensure**: `make p-gen` works seamlessly
140+
- **Test**: Recreation process from `.prompt/` files
141+
- **Validate**: Multi-problem type generation
142+
143+
## Key Template Variables to Ensure
144+
145+
```json
146+
{
147+
"problem_name": "snake_case_name",
148+
"class_name": "PascalCaseName",
149+
"method_name": "snake_case_method",
150+
"problem_number": "226",
151+
"problem_title": "Display Title",
152+
"difficulty": "Easy|Medium|Hard",
153+
"topics": "Comma, Separated, Topics",
154+
"tags": ["grind-75", "blind-75"],
155+
"problem_description": "Full description",
156+
"examples": [{"input": "...", "output": "..."}],
157+
"constraints": "Formatted constraints",
158+
"parameters": "typed_params: list[int]",
159+
"return_type": "TreeNode | None",
160+
"imports": "from leetcode_py.tree_node import TreeNode",
161+
"test_cases": [{"args": [...], "expected": ...}]
162+
}
163+
```
164+
165+
## Success Criteria
166+
167+
### Phase-by-Phase Validation (File-by-File)
168+
169+
1. ✅ **Phase 1**: `__init__.py` files generated correctly
170+
2. ✅ **Phase 2**: `solution.py` with modern syntax (`TreeNode | None`)
171+
3. ✅ **Phase 3**: `tests.py` with relative imports and clean structure
172+
4. ✅ **Phase 4**: `README.md` with clean formatting
173+
5. ✅ **Phase 5**: `playground.ipynb` with clean cells
174+
175+
### Multi-Problem Type Validation
176+
177+
5. ✅ Basic problems (array/string) generate correctly
178+
6. ✅ Tree problems generate correctly
179+
7. ✅ Different return types handled (`bool`, `int`, `str`, `list`, etc.)
180+
181+
### Automated Validation
182+
183+
8. ✅ Automated diff shows no differences vs `leetcode/.example/`
184+
9. ✅ `make p-validate` passes for all problem types
185+
10. ✅ Recreation from `.prompt/` works flawlessly
186+
11. ✅ All linting passes (`make lint`)
187+
12. ✅ Tests run successfully (`make p-test`)
188+
189+
## Files to Modify
190+
191+
### Template Files
192+
193+
1. `.templates/leetcode/{{cookiecutter.problem_name}}/`
194+
- **Add**: `__init__.py` (empty file)
195+
- **Update**: `solution.py` (modern syntax, imports)
196+
- **Update**: `tests.py` (match `leetcode/.example/` format)
197+
- **Update**: `README.md` (match `leetcode/.example/` format)
198+
- **Update**: `playground.ipynb` (match structure)
199+
200+
### Configuration
201+
202+
2. `.templates/leetcode/cookiecutter.json`
203+
- Align with JSON5 examples
204+
- Add missing variables
205+
206+
### Generation Logic
207+
208+
3. `.templates/leetcode/gen.py`
209+
- Handle `__init__.py` generation
210+
- Maintain existing variable mapping
211+
212+
### Validation Tools
213+
214+
4. **Reusable**: `.amazonq/plan/compare_template_files.py` (handles both template and generated comparisons)
215+
5. **New**: Makefile target `make p-validate`
216+
217+
## Risk Mitigation
218+
219+
- **Incremental phases** prevent all-or-nothing failures
220+
- **Automated validation** catches regressions immediately
221+
- **Multi-problem testing** ensures template generalization
222+
- **Explicit file comparison** documents exact requirements
223+
224+
This plan ensures the template generates files that exactly match `leetcode/.example/` while maintaining the robust generation process described in the rules.

.amazonq/rules/development-rules.md

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,27 @@
22

33
## Discussion Mode
44

5-
- **Discussion Mode**: Prefix prompt with "D:" to enter read-only discussion mode
6-
- In discussion mode: NO code updates, only read files and provide analysis/suggestions
7-
- Always start responses with "[Discussion Mode]" header when in discussion mode
8-
- Never exit discussion mode automatically - only when user uses "XD:" prefix
9-
- If user seems to want code changes, remind them to use "XD:" to exit discussion mode
10-
- **Exit Discussion**: Use "XD:" prefix to exit discussion mode and resume normal operations
5+
- **Enter**: Prefix with "D:" for read-only analysis mode
6+
- **Exit**: Use "XD:" to resume normal operations
7+
- In discussion mode: NO code updates, only analysis/suggestions
118

129
## Code Standards
1310

14-
- Use snake_case for Python method names (following Python convention)
15-
- Always include type hints for function parameters and return types
16-
- Use PEP 585/604 syntax: `list[str]`, `dict[str, int]`, `Type | None`, etc.
17-
- Add return statements to satisfy type checkers even if unreachable
18-
- Follow the project's linting rules (black, isort, ruff, mypy)
19-
20-
## Template Usage
21-
22-
- **When user copies LeetCode problem**: Use `leetcode/_template/` to structure the question
23-
- Copy template files to new question directory: `leetcode/{question_name}/`
24-
- Replace template placeholders with actual problem details:
25-
- `{method_name}` - snake_case method name (e.g., `two_sum`)
26-
- `{ClassName}` - PascalCase class name (e.g., `TwoSum`)
27-
- `{parameters}` - method parameters with types
28-
- `{return_type}` - return type annotation
29-
- Test case placeholders with actual examples
30-
- **Template Implementation**: Do NOT implement the Solution class - only provide test cases and structure
31-
- **Helper Functions/Classes**: If the question relies on underlying helper functions or classes (e.g., TreeNode, ListNode):
32-
- First check if implementation already exists in `leetcode_py/common/` directory
33-
- If found, import from common module
34-
- If not found, create shared implementation in `leetcode_py/common/` for reusable classes
35-
- For question-specific helpers, implement directly in the solution file
36-
- **Update Makefile**: When adding new question, update the default `QUESTION` value in Makefile to the new question name
37-
- Always use the template structure for consistency
11+
- Use snake_case for Python methods
12+
- Include type hints: `list[str]`, `dict[str, int]`, `Type | None`
13+
- Follow linting rules (black, isort, ruff, mypy)
3814

39-
## File Structure
15+
## Testing
4016

41-
Each LeetCode problem should have:
17+
- Test specific: `make p-test PROBLEM=<name>`
18+
- Test all: `make test`
19+
- Beautiful logging with loguru
4220

43-
- `README.md` - Problem description and examples
44-
- `solution.py` - Solution implementation
45-
- `tests.py` - Parametrized pytest tests with loguru logging
46-
- `__init__.py` - Empty file for Python package
21+
## File Structure
4722

48-
## Testing
23+
Each problem has:
4924

50-
- Use `make test-question QUESTION=<question_name>` to run tests
51-
- Use `make test` to run all questions with coverage
52-
- Default question is set to `two_sum` in Makefile
53-
- Tests should cover all provided examples
54-
- Use loguru for beautiful logging in tests
25+
- `README.md` - Problem description
26+
- `solution.py` - Implementation with TODO placeholder
27+
- `tests.py` - Parametrized pytest tests
28+
- `__init__.py` - Empty package file

0 commit comments

Comments
 (0)