add base file

This commit is contained in:
2025-12-09 15:28:06 +01:00
commit ce281d7952
3 changed files with 495 additions and 0 deletions

154
README.md Normal file
View File

@@ -0,0 +1,154 @@
# Simple Todo Application
A command-line todo list manager with persistent JSON storage.
## Overview
This is a simple yet complete todo application that demonstrates:
- Functional requirements implementation (CRUD operations)
- Non-functional requirements (persistence, validation, performance, error handling)
- Clean code architecture with proper error handling
- Data persistence using JSON
## Requirements Implemented
### Functional Requirements
- **REQ-001**: Create Todo Items - Add new todos with title and optional description
- **REQ-002**: List Todo Items - Display all todos with status and metadata
- **REQ-003**: Mark Todo as Complete - Toggle completion status on/off
- **REQ-004**: Delete Todo Items - Remove todos from the list permanently
- **REQ-005**: Edit Todo Items - Update title and description of existing todos
### Non-Functional Requirements
- **REQ-006**: Data Persistence - All todos saved to `todos.json` file
- **REQ-007**: Input Validation - Titles validated (non-empty, max 200 chars)
- **REQ-008**: Performance - All operations complete within 100ms
- **REQ-009**: Code Quality - Comprehensive error handling with descriptive messages
- **REQ-010**: User Interface - Simple CLI with menu-driven interface
## Features
✅ Create, Read, Update, Delete (CRUD) todos
✅ Toggle completion status
✅ Persistent storage (JSON)
✅ Input validation
✅ Error handling
✅ Clear CLI interface
✅ Timestamps for created/updated dates
## Usage
```bash
python app.py
```
### Menu Options
```
1. Create new todo - Add a new todo item
2. List all todos - View all todos with status
3. Mark todo complete - Toggle todo completion status
4. Edit todo - Update title or description
5. Delete todo - Remove a todo permanently
6. Exit - Save and quit application
```
## Data Storage
All todos are persisted in `todos.json` with the following structure:
```json
[
{
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"completed": false,
"created_at": "2025-12-09T10:30:00",
"updated_at": "2025-12-09T10:30:00"
}
]
```
## File Structure
```
simple_app/
├── requirements.md - 10 requirements for the application
├── app.py - Main application implementation
└── README.md - This file
```
## Requirements Traceability
Each requirement is implemented and can be traced in the code:
| REQ ID | Feature | Implementation | Status |
|--------|---------|-----------------|--------|
| REQ-001 | Create todos | `create_todo()` method | ✅ |
| REQ-002 | List todos | `list_todos()` method | ✅ |
| REQ-003 | Mark complete | `mark_complete()` method | ✅ |
| REQ-004 | Delete todos | `delete_todo()` method | ✅ |
| REQ-005 | Edit todos | `edit_todo()` method | ✅ |
| REQ-006 | Data persistence | `_load_todos()`, `_save_todos()` | ✅ |
| REQ-007 | Input validation | `_validate_title()` method | ✅ |
| REQ-008 | Performance | JSON operations, efficient filtering | ✅ |
| REQ-009 | Error handling | Try-except blocks, error messages | ✅ |
| REQ-010 | CLI interface | `show_menu()`, `run()` methods | ✅ |
## Example Session
```
🚀 Welcome to the Simple Todo Application!
════════════════════════════════════════════════════════════════════
📝 Todo Application
════════════════════════════════════════════════════════════════════
1. Create new todo
2. List all todos
3. Mark todo complete/incomplete
4. Edit todo
5. Delete todo
6. Exit
════════════════════════════════════════════════════════════════════
Enter your choice (1-6): 1
Enter todo title: Buy groceries
Enter description (optional): Milk, eggs, bread
✅ Todo created: 'Buy groceries'
📋 Your Todos:
════════════════════════════════════════════════════════════════════
○ [1] [TODO] Buy groceries
Description: Milk, eggs, bread
Created: 2025-12-09
════════════════════════════════════════════════════════════════════
```
## Error Handling
The application provides clear error messages for:
- Empty titles
- Titles exceeding 200 characters
- Invalid todo IDs
- File I/O errors
- Invalid menu selections
## Performance
All operations are optimized:
- JSON file read on startup only
- Linear search for todo lookups (acceptable for typical use)
- Single file write per operation
- No external dependencies
## Code Quality
- Type hints for all function parameters
- Comprehensive docstrings
- Clear variable names
- Proper error handling throughout
- Separation of concerns (UI, data, logic)

306
app.py Normal file
View File

@@ -0,0 +1,306 @@
#!/usr/bin/env python3
"""
Simple Todo Application
This application implements a command-line todo list manager that persists
data to JSON and provides full CRUD operations with validation.
Implements requirements: REQ-001 through REQ-010
"""
import json
import os
import sys
from pathlib import Path
from typing import Optional, List, Dict, Any
from datetime import datetime
class TodoApp:
"""
Simple Todo Application with persistent storage.
Implements all 10 requirements:
- REQ-001: Create todo items
- REQ-002: List todo items
- REQ-003: Mark complete/incomplete
- REQ-004: Delete todo items
- REQ-005: Edit todo items
- REQ-006: Data persistence (JSON)
- REQ-007: Input validation
- REQ-008: Performance optimization
- REQ-009: Error handling
- REQ-010: CLI user interface
"""
DATA_FILE = "todos.json"
MAX_TITLE_LENGTH = 200
def __init__(self):
"""Initialize the application with data persistence."""
self.todos: List[Dict[str, Any]] = []
self._load_todos()
def _load_todos(self) -> None:
"""Load todos from JSON file if it exists. REQ-006: Data Persistence."""
try:
if Path(self.DATA_FILE).exists():
with open(self.DATA_FILE, 'r') as f:
self.todos = json.load(f)
except (json.JSONDecodeError, IOError) as e:
print(f"⚠️ Could not load todos: {e}. Starting with empty list.")
self.todos = []
def _save_todos(self) -> None:
"""Save todos to JSON file. REQ-006: Data Persistence."""
try:
with open(self.DATA_FILE, 'w') as f:
json.dump(self.todos, f, indent=2)
except IOError as e:
raise RuntimeError(f"Failed to save todos: {e}") # REQ-009: Error handling
def _validate_title(self, title: str) -> bool:
"""
Validate todo title. REQ-007: Input Validation.
Args:
title: The title to validate
Returns:
bool: True if valid, False otherwise
"""
if not title or not title.strip():
print("❌ Error: Title cannot be empty")
return False
if len(title) > self.MAX_TITLE_LENGTH:
print(f"❌ Error: Title cannot exceed {self.MAX_TITLE_LENGTH} characters")
return False
return True
def create_todo(self, title: str, description: str = "") -> bool:
"""
Create a new todo item. REQ-001: Create Todo Items.
REQ-007: Input Validation. REQ-009: Error handling.
Args:
title: The todo title
description: Optional todo description
Returns:
bool: True if created successfully
"""
if not self._validate_title(title):
return False
try:
todo = {
"id": len(self.todos) + 1,
"title": title.strip(),
"description": description.strip(),
"completed": False,
"created_at": datetime.now().isoformat(),
"updated_at": datetime.now().isoformat()
}
self.todos.append(todo)
self._save_todos()
print(f"✅ Todo created: '{title}'")
return True
except Exception as e:
print(f"❌ Error creating todo: {e}") # REQ-009: Error handling
return False
def list_todos(self) -> None:
"""
Display all todos in a formatted list. REQ-002: List Todo Items.
REQ-010: CLI user interface.
"""
if not self.todos:
print("\n📋 No todos yet. Create one to get started!")
return
print("\n📋 Your Todos:")
print("=" * 70)
for todo in self.todos:
status = "" if todo["completed"] else ""
completed_text = "[DONE]" if todo["completed"] else "[TODO]"
print(f"\n {status} [{todo['id']}] {completed_text} {todo['title']}")
if todo["description"]:
print(f" Description: {todo['description']}")
print(f" Created: {todo['created_at'][:10]}")
print("\n" + "=" * 70)
def mark_complete(self, todo_id: int) -> bool:
"""
Mark a todo as complete or toggle completion status.
REQ-003: Mark Todo as Complete. REQ-009: Error handling.
Args:
todo_id: The ID of the todo to mark complete
Returns:
bool: True if successful
"""
try:
for todo in self.todos:
if todo["id"] == todo_id:
todo["completed"] = not todo["completed"]
todo["updated_at"] = datetime.now().isoformat()
self._save_todos()
status = "completed" if todo["completed"] else "reopened"
print(f"✅ Todo {status}: '{todo['title']}'")
return True
print(f"❌ Error: Todo with ID {todo_id} not found")
return False
except Exception as e:
print(f"❌ Error marking todo: {e}") # REQ-009: Error handling
return False
def delete_todo(self, todo_id: int) -> bool:
"""
Delete a todo item. REQ-004: Delete Todo Items.
REQ-009: Error handling.
Args:
todo_id: The ID of the todo to delete
Returns:
bool: True if successful
"""
try:
original_length = len(self.todos)
self.todos = [t for t in self.todos if t["id"] != todo_id]
if len(self.todos) < original_length:
self._save_todos()
print(f"✅ Todo deleted")
return True
else:
print(f"❌ Error: Todo with ID {todo_id} not found")
return False
except Exception as e:
print(f"❌ Error deleting todo: {e}") # REQ-009: Error handling
return False
def edit_todo(self, todo_id: int, title: Optional[str] = None,
description: Optional[str] = None) -> bool:
"""
Edit an existing todo item. REQ-005: Edit Todo Items.
REQ-007: Input Validation. REQ-009: Error handling.
Args:
todo_id: The ID of the todo to edit
title: New title (optional)
description: New description (optional)
Returns:
bool: True if successful
"""
try:
for todo in self.todos:
if todo["id"] == todo_id:
if title is not None:
if not self._validate_title(title):
return False
todo["title"] = title.strip()
if description is not None:
todo["description"] = description.strip()
todo["updated_at"] = datetime.now().isoformat()
self._save_todos()
print(f"✅ Todo updated: '{todo['title']}'")
return True
print(f"❌ Error: Todo with ID {todo_id} not found")
return False
except Exception as e:
print(f"❌ Error editing todo: {e}") # REQ-009: Error handling
return False
def show_menu(self) -> None:
"""Display the main menu. REQ-010: CLI user interface."""
print("\n" + "=" * 70)
print("📝 Todo Application")
print("=" * 70)
print("1. Create new todo")
print("2. List all todos")
print("3. Mark todo complete/incomplete")
print("4. Edit todo")
print("5. Delete todo")
print("6. Exit")
print("=" * 70)
def run(self) -> None:
"""Main application loop. REQ-010: CLI user interface."""
print("\n🚀 Welcome to the Simple Todo Application!")
while True:
self.show_menu()
choice = input("Enter your choice (1-6): ").strip()
if choice == "1":
# REQ-001: Create Todo Items
title = input("Enter todo title: ").strip()
description = input("Enter description (optional): ").strip()
self.create_todo(title, description)
elif choice == "2":
# REQ-002: List Todo Items
self.list_todos()
elif choice == "3":
# REQ-003: Mark Todo as Complete
self.list_todos()
try:
todo_id = int(input("Enter todo ID to toggle: "))
self.mark_complete(todo_id)
except ValueError:
print("❌ Error: Please enter a valid ID number")
elif choice == "4":
# REQ-005: Edit Todo Items
self.list_todos()
try:
todo_id = int(input("Enter todo ID to edit: "))
title = input("Enter new title (leave empty to skip): ").strip()
description = input("Enter new description (leave empty to skip): ").strip()
new_title = title if title else None
new_description = description if description else None
self.edit_todo(todo_id, new_title, new_description)
except ValueError:
print("❌ Error: Please enter a valid ID number")
elif choice == "5":
# REQ-004: Delete Todo Items
self.list_todos()
try:
todo_id = int(input("Enter todo ID to delete: "))
confirm = input(f"Are you sure? (yes/no): ").strip().lower()
if confirm == "yes":
self.delete_todo(todo_id)
except ValueError:
print("❌ Error: Please enter a valid ID number")
elif choice == "6":
# REQ-010: CLI user interface - graceful exit
print("\n👋 Goodbye! Your todos have been saved.")
break
else:
print("❌ Error: Invalid choice. Please enter 1-6.")
if __name__ == "__main__":
app = TodoApp()
app.run()

35
requirements.md Normal file
View File

@@ -0,0 +1,35 @@
# Simple Todo Application - Requirements
## Functional Requirements
### REQ-001: Create Todo Items
The application shall allow users to create new todo items with a title and optional description.
### REQ-002: List Todo Items
The application shall display all created todo items in a list format with their current status.
### REQ-003: Mark Todo as Complete
The application shall provide functionality to mark a todo item as complete or incomplete with a toggle action.
### REQ-004: Delete Todo Items
The application shall allow users to delete todo items from the list permanently.
### REQ-005: Edit Todo Items
The application shall allow users to edit the title and description of existing todo items.
## Non-Functional Requirements
### REQ-006: Data Persistence
The application shall persist all todo items to a JSON file so that data remains available after application restart.
### REQ-007: Input Validation
The application shall validate that todo titles are not empty and are no longer than 200 characters.
### REQ-008: Performance
The application shall complete all todo operations (create, read, update, delete) within 100 milliseconds.
### REQ-009: Code Quality
The application shall implement proper error handling with descriptive error messages for all operations.
### REQ-010: User Interface
The application shall provide a simple command-line interface with clear menu options and status feedback for user actions.