commit ce281d7952a25e0cf4ce30f151eb5d2f1b1a0d56 Author: Sebastian Sucker Date: Tue Dec 9 15:28:06 2025 +0100 add base file diff --git a/README.md b/README.md new file mode 100644 index 0000000..25c45be --- /dev/null +++ b/README.md @@ -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) diff --git a/app.py b/app.py new file mode 100644 index 0000000..f816eef --- /dev/null +++ b/app.py @@ -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() diff --git a/requirements.md b/requirements.md new file mode 100644 index 0000000..869fb46 --- /dev/null +++ b/requirements.md @@ -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.