Files
TinyTorch/tito/commands/reset.py
Vijay Janapa Reddi a13b4f7244 Improve SIGCSE paper with reviewer feedback and clean up repository
Paper improvements:
- Add differentiated time estimates (60-80h experienced, 100-120h typical, 140-180h struggling)
- Moderate cognitive load claims with hedging language and empirical validation notes
- Add ML Systems Research subsection with citations (Baydin AD survey, Chen gradient checkpointing, TVM, FlashAttention)
- Add comprehensive Threats to Validity section (selection bias, single institution, demand characteristics, no control group, maturation, assessment validity)
- Define jargon (monkey-patching) at first use with clear explanation

Documentation updates:
- Restructure TITO CLI docs into dedicated section (overview, modules, milestones, data, troubleshooting)
- Update student workflow guide and quickstart guide
- Remove deprecated files (testing-framework.md, tito-essentials.md)
- Update module template and testing architecture docs

Repository cleanup:
- Remove temporary review files (ADDITIONAL_REVIEWS.md, EDTECH_OPENSOURCE_REVIEWS.md, TA_STRUGGLING_STUDENT_REVIEWS.md, etc.)
- Remove temporary development planning docs
- Update demo GIFs and configurations
2025-11-16 23:46:38 -05:00

377 lines
14 KiB
Python

"""
Reset command for TinyTorch CLI: resets package and user data.
"""
import json
import shutil
from datetime import datetime
from argparse import ArgumentParser, Namespace
from pathlib import Path
from rich.panel import Panel
from rich.text import Text
from .base import BaseCommand
class ResetCommand(BaseCommand):
@property
def name(self) -> str:
return "reset"
@property
def description(self) -> str:
return "Reset package files or user progress data"
def add_arguments(self, parser: ArgumentParser) -> None:
subparsers = parser.add_subparsers(
dest='reset_command',
help='Reset subcommands',
metavar='SUBCOMMAND'
)
# Package reset (original functionality)
package_parser = subparsers.add_parser(
'package',
help='Reset tinytorch package to clean state (remove exported files)'
)
package_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
# All data reset
all_parser = subparsers.add_parser(
'all',
help='Reset all user progress (modules + milestones + config)'
)
all_parser.add_argument("--backup", action="store_true", help="Create backup before reset")
all_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
# Progress reset
progress_parser = subparsers.add_parser(
'progress',
help='Reset module completion tracking only'
)
progress_parser.add_argument("--backup", action="store_true", help="Create backup before reset")
progress_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
# Milestones reset
milestones_parser = subparsers.add_parser(
'milestones',
help='Reset milestone achievements only'
)
milestones_parser.add_argument("--backup", action="store_true", help="Create backup before reset")
milestones_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
# Config reset
config_parser = subparsers.add_parser(
'config',
help='Reset configuration to defaults'
)
config_parser.add_argument("--force", action="store_true", help="Skip confirmation prompt")
def run(self, args: Namespace) -> int:
console = self.console
if not hasattr(args, 'reset_command') or not args.reset_command:
console.print(Panel(
"[bold cyan]Reset Commands[/bold cyan]\n\n"
"Available subcommands:\n"
" • [bold]package[/bold] - Reset tinytorch package (remove exported files)\n"
" • [bold]all[/bold] - Reset all user progress (modules + milestones + config)\n"
" • [bold]progress[/bold] - Reset module completion tracking only\n"
" • [bold]milestones[/bold] - Reset milestone achievements only\n"
" • [bold]config[/bold] - Reset configuration to defaults\n\n"
"[dim]Example: tito reset progress --backup[/dim]",
title="Reset Command Group",
border_style="bright_yellow"
))
return 0
# Execute the appropriate subcommand
if args.reset_command == 'package':
return self._reset_package(args)
elif args.reset_command == 'all':
return self._reset_all(args)
elif args.reset_command == 'progress':
return self._reset_progress(args)
elif args.reset_command == 'milestones':
return self._reset_milestones(args)
elif args.reset_command == 'config':
return self._reset_config(args)
else:
console.print(Panel(
f"[red]Unknown reset subcommand: {args.reset_command}[/red]",
title="Error",
border_style="red"
))
return 1
def _reset_package(self, args: Namespace) -> int:
"""Reset tinytorch package (original functionality)."""
console = self.console
console.print(Panel("🔄 Resetting TinyTorch Package",
title="Package Reset", border_style="bright_yellow"))
tinytorch_path = Path("tinytorch")
if not tinytorch_path.exists():
console.print(Panel("[yellow]⚠️ TinyTorch package directory not found. Nothing to reset.[/yellow]",
title="Nothing to Reset", border_style="yellow"))
return 0
# Ask for confirmation unless --force is used
if not args.force:
console.print("\n[yellow]This will remove all exported Python files from the tinytorch package.[/yellow]")
console.print("[yellow]Notebooks in modules/ will be preserved.[/yellow]\n")
try:
response = input("Are you sure you want to reset? (y/N): ").strip().lower()
if response not in ['y', 'yes']:
console.print(Panel("[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
except KeyboardInterrupt:
console.print(Panel("[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
reset_text = Text()
reset_text.append("🗑️ Removing all exported files:\n", style="bold red")
# Simple approach: remove all .py files except __init__.py
files_removed = 0
for py_file in tinytorch_path.rglob("*.py"):
if py_file.name != "__init__.py":
try:
rel_path = py_file.relative_to(tinytorch_path)
reset_text.append(f" 🗑️ tinytorch/{rel_path}\n", style="red")
py_file.unlink()
files_removed += 1
except Exception as e:
reset_text.append(f" ❌ Failed to remove {py_file}: {e}\n", style="red")
# Remove __pycache__ directories
for pycache in tinytorch_path.rglob("__pycache__"):
if pycache.is_dir():
reset_text.append(f" 🗑️ {pycache}/\n", style="red")
shutil.rmtree(pycache)
# Remove .pytest_cache if it exists
pytest_cache = Path(".pytest_cache")
if pytest_cache.exists():
reset_text.append(f" 🗑️ .pytest_cache/\n", style="red")
shutil.rmtree(pytest_cache)
if files_removed > 0:
reset_text.append(f"\n✅ Reset complete! Removed {files_removed} generated files.\n", style="bold green")
reset_text.append("\n💡 Next steps:\n", style="bold yellow")
reset_text.append(" • Run: tito module complete 01 - Re-export modules\n", style="white")
console.print(Panel(reset_text, title="Reset Complete", border_style="green"))
else:
console.print(Panel("[yellow]No generated files found to remove.[/yellow]",
title="Nothing to Reset", border_style="yellow"))
return 0
def _create_backup(self) -> Path:
"""Create timestamped backup of .tito folder."""
console = self.console
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = Path(f".tito_backup_{timestamp}")
tito_dir = Path(".tito")
if tito_dir.exists():
shutil.copytree(tito_dir, backup_dir)
console.print(f"[green]✅ Backup created: {backup_dir}[/green]")
return backup_dir
def _reset_all(self, args: Namespace) -> int:
"""Reset all user progress data."""
console = self.console
# Ask for confirmation
if not args.force:
console.print("\n[bold red]⚠️ Warning: This will reset ALL progress[/bold red]\n")
console.print("[yellow]This will clear:[/yellow]")
console.print(" • Module completion tracking")
console.print(" • Milestone achievements")
console.print(" • Configuration settings\n")
console.print("[dim]Your code in modules/ will NOT be deleted.[/dim]\n")
try:
response = input("Continue? (y/N): ").strip().lower()
if response not in ['y', 'yes']:
console.print(Panel("[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
except KeyboardInterrupt:
console.print(Panel("\n[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
# Create backup if requested
if args.backup:
self._create_backup()
# Reset all data files
tito_dir = Path(".tito")
tito_dir.mkdir(parents=True, exist_ok=True)
# Reset progress.json
progress_file = tito_dir / "progress.json"
progress_file.write_text(json.dumps({
"version": "1.0",
"completed_modules": [],
"completion_dates": {}
}, indent=2))
# Reset milestones.json
milestones_file = tito_dir / "milestones.json"
milestones_file.write_text(json.dumps({
"version": "1.0",
"completed_milestones": [],
"completion_dates": {}
}, indent=2))
# Reset config.json
config_file = tito_dir / "config.json"
config_file.write_text(json.dumps({
"logo_theme": "standard"
}, indent=2))
console.print(Panel(
"[green]✅ All progress reset![/green]\n\n"
"You're ready to start fresh.\\n"
"Run: [cyan]tito module start 01[/cyan]",
title="🔄 Reset Complete",
border_style="green"
))
return 0
def _reset_progress(self, args: Namespace) -> int:
"""Reset module completion tracking only."""
console = self.console
# Ask for confirmation
if not args.force:
console.print("\n[bold yellow]⚠️ This will reset module completion tracking[/bold yellow]\n")
console.print("[dim]Milestone achievements will be preserved.[/dim]\n")
try:
response = input("Continue? (y/N): ").strip().lower()
if response not in ['y', 'yes']:
console.print(Panel("[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
except KeyboardInterrupt:
console.print(Panel("\n[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
# Create backup if requested
if args.backup:
self._create_backup()
# Reset progress.json
tito_dir = Path(".tito")
tito_dir.mkdir(parents=True, exist_ok=True)
progress_file = tito_dir / "progress.json"
progress_file.write_text(json.dumps({
"version": "1.0",
"completed_modules": [],
"completion_dates": {}
}, indent=2))
console.print(Panel(
"[green]✅ Module progress reset![/green]\n\n"
"You can re-complete modules with:\\n"
"[cyan]tito module complete XX[/cyan]",
title="🔄 Progress Reset",
border_style="green"
))
return 0
def _reset_milestones(self, args: Namespace) -> int:
"""Reset milestone achievements only."""
console = self.console
# Ask for confirmation
if not args.force:
console.print("\n[bold yellow]⚠️ This will reset milestone achievements[/bold yellow]\n")
console.print("[dim]Module completion will be preserved.[/dim]\n")
try:
response = input("Continue? (y/N): ").strip().lower()
if response not in ['y', 'yes']:
console.print(Panel("[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
except KeyboardInterrupt:
console.print(Panel("\n[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
# Create backup if requested
if args.backup:
self._create_backup()
# Reset milestones.json
tito_dir = Path(".tito")
tito_dir.mkdir(parents=True, exist_ok=True)
milestones_file = tito_dir / "milestones.json"
milestones_file.write_text(json.dumps({
"version": "1.0",
"completed_milestones": [],
"completion_dates": {}
}, indent=2))
console.print(Panel(
"[green]✅ Milestone achievements reset![/green]\n\n"
"You can re-run milestones with:\\n"
"[cyan]tito milestone run XX[/cyan]",
title="🔄 Milestones Reset",
border_style="green"
))
return 0
def _reset_config(self, args: Namespace) -> int:
"""Reset configuration to defaults."""
console = self.console
# Ask for confirmation
if not args.force:
console.print("\n[bold yellow]⚠️ This will reset configuration to defaults[/bold yellow]\n")
try:
response = input("Continue? (y/N): ").strip().lower()
if response not in ['y', 'yes']:
console.print(Panel("[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
except KeyboardInterrupt:
console.print(Panel("\n[cyan]Reset cancelled.[/cyan]",
title="Cancelled", border_style="cyan"))
return 0
# Reset config.json
tito_dir = Path(".tito")
tito_dir.mkdir(parents=True, exist_ok=True)
config_file = tito_dir / "config.json"
config_file.write_text(json.dumps({
"logo_theme": "standard"
}, indent=2))
console.print(Panel(
"[green]✅ Configuration reset to defaults![/green]",
title="🔄 Config Reset",
border_style="green"
))
return 0