Daily Tech Brief

Top startup stories in your inbox

Subscribe Free

Β© 2026 rakrisi Daily

Project 1 - Task Manager CLI

Project 1: Task Manager CLI

Welcome to your first complete Python application! We’re building a command-line task management system that helps users organize their tasks, set priorities, track due dates, and stay productive.

Project Overview

TaskMaster CLI is a command-line application that allows users to:

  • βœ… Create, read, update, and delete tasks
  • βœ… Organize tasks by categories and priorities
  • βœ… Set due dates and get reminders
  • βœ… Search and filter tasks
  • βœ… Persist data between sessions
  • βœ… Export tasks to different formats

Learning Objectives

By the end of this project, you’ll be able to:

  • Design and implement object-oriented applications
  • Handle file I/O for data persistence
  • Create professional command-line interfaces
  • Implement CRUD operations
  • Work with dates and times
  • Write modular, maintainable code

Project Requirements

Core Features

  1. Task Management

    • Add new tasks with title, description, category, priority, due date
    • View all tasks or filter by criteria
    • Edit existing tasks
    • Mark tasks as complete/incomplete
    • Delete tasks
  2. Organization

    • Categorize tasks (Work, Personal, Shopping, Health, etc.)
    • Set priorities (High, Medium, Low)
    • Sort tasks by due date, priority, or creation date
  3. Data Persistence

    • Save tasks to JSON file
    • Load tasks on application startup
    • Automatic saving after changes
  4. Search & Filter

    • Search tasks by title or description
    • Filter by category, priority, completion status
    • Show overdue tasks

Advanced Features

  1. Due Date Management

    • Set and display due dates
    • Show days remaining until due
    • Highlight overdue tasks
  2. Export Functionality

    • Export tasks to CSV format
    • Export completed tasks summary
  3. Statistics

    • Show task completion statistics
    • Display productivity metrics

Project Structure

taskmaster/
β”œβ”€β”€ taskmaster/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ cli.py              # Command-line interface
β”‚   β”œβ”€β”€ task.py             # Task model class
β”‚   β”œβ”€β”€ task_manager.py     # Business logic
β”‚   β”œβ”€β”€ storage.py          # Data persistence
β”‚   └── utils.py            # Helper functions
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ test_task.py
β”‚   β”œβ”€β”€ test_task_manager.py
β”‚   └── test_storage.py
β”œβ”€β”€ data/
β”‚   └── tasks.json          # Task storage
β”œβ”€β”€ docs/
β”‚   └── README.md
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ setup.py
└── main.py                 # Application entry point

Step 1: Design the Task Model

Let’s start by creating the core Task class that represents individual tasks.

# taskmaster/task.py
from datetime import datetime, date
from typing import Optional
import json

class Task:
    """Represents a single task with all its properties."""
    
    PRIORITIES = ['Low', 'Medium', 'High']
    CATEGORIES = ['Work', 'Personal', 'Shopping', 'Health', 'Education', 'Other']
    
    def __init__(self, title: str, description: str = "", 
                 category: str = "Other", priority: str = "Medium",
                 due_date: Optional[date] = None, task_id: Optional[int] = None):
        self.task_id = task_id or self._generate_id()
        self.title = title
        self.description = description
        self.category = category
        self.priority = priority
        self.due_date = due_date
        self.completed = False
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        
        # Validate inputs
        self._validate_category()
        self._validate_priority()
    
    def _generate_id(self) -> int:
        """Generate a unique task ID."""
        return int(datetime.now().timestamp() * 1000000) % 1000000
    
    def _validate_category(self):
        """Ensure category is valid."""
        if self.category not in self.CATEGORIES:
            raise ValueError(f"Category must be one of: {', '.join(self.CATEGORIES)}")
    
    def _validate_priority(self):
        """Ensure priority is valid."""
        if self.priority not in self.PRIORITIES:
            raise ValueError(f"Priority must be one of: {', '.join(self.PRIORITIES)}")
    
    def mark_complete(self):
        """Mark the task as completed."""
        self.completed = True
        self.updated_at = datetime.now()
    
    def mark_incomplete(self):
        """Mark the task as incomplete."""
        self.completed = False
        self.updated_at = datetime.now()
    
    def update(self, **kwargs):
        """Update task properties."""
        allowed_fields = ['title', 'description', 'category', 'priority', 'due_date']
        
        for field, value in kwargs.items():
            if field in allowed_fields:
                if field == 'category':
                    self._validate_category_value(value)
                elif field == 'priority':
                    self._validate_priority_value(value)
                setattr(self, field, value)
        
        self.updated_at = datetime.now()
    
    def _validate_category_value(self, category: str):
        """Validate category value."""
        if category not in self.CATEGORIES:
            raise ValueError(f"Category must be one of: {', '.join(self.CATEGORIES)}")
    
    def _validate_priority_value(self, priority: str):
        """Validate priority value."""
        if priority not in self.PRIORITIES:
            raise ValueError(f"Priority must be one of: {', '.join(self.PRIORITIES)}")
    
    def is_overdue(self) -> bool:
        """Check if task is overdue."""
        if self.due_date and not self.completed:
            return self.due_date < date.today()
        return False
    
    def days_until_due(self) -> Optional[int]:
        """Calculate days until due date."""
        if self.due_date and not self.completed:
            return (self.due_date - date.today()).days
        return None
    
    def to_dict(self) -> dict:
        """Convert task to dictionary for JSON serialization."""
        return {
            'task_id': self.task_id,
            'title': self.title,
            'description': self.description,
            'category': self.category,
            'priority': self.priority,
            'due_date': self.due_date.isoformat() if self.due_date else None,
            'completed': self.completed,
            'created_at': self.created_at.isoformat(),
            'updated_at': self.updated_at.isoformat()
        }
    
    @classmethod
    def from_dict(cls, data: dict) -> 'Task':
        """Create task from dictionary."""
        # Parse dates
        due_date = None
        if data.get('due_date'):
            due_date = date.fromisoformat(data['due_date'])
        
        created_at = datetime.fromisoformat(data['created_at'])
        updated_at = datetime.fromisoformat(data['updated_at'])
        
        # Create task
        task = cls(
            title=data['title'],
            description=data.get('description', ''),
            category=data.get('category', 'Other'),
            priority=data.get('priority', 'Medium'),
            due_date=due_date,
            task_id=data['task_id']
        )
        
        # Set additional properties
        task.completed = data.get('completed', False)
        task.created_at = created_at
        task.updated_at = updated_at
        
        return task
    
    def __str__(self) -> str:
        """String representation of the task."""
        status = "βœ“" if self.completed else "β—‹"
        due_info = ""
        if self.due_date:
            days = self.days_until_due()
            if days is not None:
                if days < 0:
                    due_info = f" (OVERDUE: {abs(days)} days)"
                elif days == 0:
                    due_info = " (DUE TODAY)"
                else:
                    due_info = f" ({days} days left)"
        
        return f"{status} [{self.priority}] {self.title} - {self.category}{due_info}"
    
    def __repr__(self) -> str:
        return f"Task(id={self.task_id}, title='{self.title}', completed={self.completed})"

Step 2: Implement Data Storage

Create the storage layer for persisting tasks to JSON files.

# taskmaster/storage.py
import json
import os
from typing import List, Dict, Any
from .task import Task

class TaskStorage:
    """Handles data persistence for tasks."""
    
    def __init__(self, data_dir: str = "data", filename: str = "tasks.json"):
        self.data_dir = data_dir
        self.filename = os.path.join(data_dir, filename)
        self._ensure_data_dir()
    
    def _ensure_data_dir(self):
        """Ensure data directory exists."""
        if not os.path.exists(self.data_dir):
            os.makedirs(self.data_dir)
    
    def save_tasks(self, tasks: List[Task]) -> bool:
        """Save tasks to JSON file."""
        try:
            task_data = [task.to_dict() for task in tasks]
            with open(self.filename, 'w', encoding='utf-8') as f:
                json.dump(task_data, f, indent=2, ensure_ascii=False)
            return True
        except Exception as e:
            print(f"Error saving tasks: {e}")
            return False
    
    def load_tasks(self) -> List[Task]:
        """Load tasks from JSON file."""
        if not os.path.exists(self.filename):
            return []
        
        try:
            with open(self.filename, 'r', encoding='utf-8') as f:
                task_data = json.load(f)
            
            tasks = []
            for data in task_data:
                try:
                    task = Task.from_dict(data)
                    tasks.append(task)
                except Exception as e:
                    print(f"Error loading task {data.get('task_id', 'unknown')}: {e}")
            
            return tasks
        except Exception as e:
            print(f"Error loading tasks file: {e}")
            return []
    
    def export_to_csv(self, tasks: List[Task], filename: str) -> bool:
        """Export tasks to CSV format."""
        try:
            import csv
            
            csv_filename = os.path.join(self.data_dir, filename)
            with open(csv_filename, 'w', newline='', encoding='utf-8') as f:
                writer = csv.writer(f)
                
                # Write header
                writer.writerow(['ID', 'Title', 'Description', 'Category', 'Priority', 
                               'Due Date', 'Completed', 'Created At', 'Updated At'])
                
                # Write tasks
                for task in tasks:
                    writer.writerow([
                        task.task_id,
                        task.title,
                        task.description,
                        task.category,
                        task.priority,
                        task.due_date.isoformat() if task.due_date else '',
                        'Yes' if task.completed else 'No',
                        task.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                        task.updated_at.strftime('%Y-%m-%d %H:%M:%S')
                    ])
            
            print(f"Tasks exported to {csv_filename}")
            return True
        except Exception as e:
            print(f"Error exporting to CSV: {e}")
            return False
    
    def get_statistics(self, tasks: List[Task]) -> Dict[str, Any]:
        """Get statistics about tasks."""
        if not tasks:
            return {'total_tasks': 0}
        
        total_tasks = len(tasks)
        completed_tasks = sum(1 for task in tasks if task.completed)
        overdue_tasks = sum(1 for task in tasks if task.is_overdue())
        
        # Category breakdown
        categories = {}
        for task in tasks:
            categories[task.category] = categories.get(task.category, 0) + 1
        
        # Priority breakdown
        priorities = {}
        for task in tasks:
            priorities[task.priority] = priorities.get(task.priority, 0) + 1
        
        return {
            'total_tasks': total_tasks,
            'completed_tasks': completed_tasks,
            'pending_tasks': total_tasks - completed_tasks,
            'overdue_tasks': overdue_tasks,
            'completion_rate': completed_tasks / total_tasks * 100 if total_tasks > 0 else 0,
            'categories': categories,
            'priorities': priorities
        }

Step 3: Create Task Manager

Implement the business logic layer that manages tasks.

# taskmaster/task_manager.py
from typing import List, Optional, Dict, Any
from datetime import date
from .task import Task
from .storage import TaskStorage

class TaskManager:
    """Manages task operations and business logic."""
    
    def __init__(self, storage: TaskStorage):
        self.storage = storage
        self.tasks = self.storage.load_tasks()
    
    def add_task(self, title: str, description: str = "", category: str = "Other",
                 priority: str = "Medium", due_date: Optional[date] = None) -> Task:
        """Add a new task."""
        task = Task(title, description, category, priority, due_date)
        self.tasks.append(task)
        self._save()
        return task
    
    def get_task(self, task_id: int) -> Optional[Task]:
        """Get a task by ID."""
        for task in self.tasks:
            if task.task_id == task_id:
                return task
        return None
    
    def update_task(self, task_id: int, **kwargs) -> bool:
        """Update a task."""
        task = self.get_task(task_id)
        if task:
            task.update(**kwargs)
            self._save()
            return True
        return False
    
    def delete_task(self, task_id: int) -> bool:
        """Delete a task."""
        task = self.get_task(task_id)
        if task:
            self.tasks.remove(task)
            self._save()
            return True
        return False
    
    def mark_complete(self, task_id: int) -> bool:
        """Mark a task as complete."""
        task = self.get_task(task_id)
        if task:
            task.mark_complete()
            self._save()
            return True
        return False
    
    def mark_incomplete(self, task_id: int) -> bool:
        """Mark a task as incomplete."""
        task = self.get_task(task_id)
        if task:
            task.mark_incomplete()
            self._save()
            return True
        return False
    
    def get_all_tasks(self, sort_by: str = "created_at", reverse: bool = False) -> List[Task]:
        """Get all tasks, optionally sorted."""
        tasks = self.tasks.copy()
        
        if sort_by == "due_date":
            tasks.sort(key=lambda t: (t.due_date is None, t.due_date), reverse=reverse)
        elif sort_by == "priority":
            priority_order = {'High': 3, 'Medium': 2, 'Low': 1}
            tasks.sort(key=lambda t: priority_order.get(t.priority, 0), reverse=reverse)
        elif sort_by == "title":
            tasks.sort(key=lambda t: t.title.lower(), reverse=reverse)
        else:  # created_at
            tasks.sort(key=lambda t: t.created_at, reverse=reverse)
        
        return tasks
    
    def search_tasks(self, query: str) -> List[Task]:
        """Search tasks by title or description."""
        query = query.lower()
        return [task for task in self.tasks 
                if query in task.title.lower() or query in task.description.lower()]
    
    def filter_tasks(self, category: Optional[str] = None, 
                    priority: Optional[str] = None, 
                    completed: Optional[bool] = None,
                    overdue: bool = False) -> List[Task]:
        """Filter tasks by various criteria."""
        filtered = self.tasks.copy()
        
        if category:
            filtered = [t for t in filtered if t.category == category]
        
        if priority:
            filtered = [t for t in filtered if t.priority == priority]
        
        if completed is not None:
            filtered = [t for t in filtered if t.completed == completed]
        
        if overdue:
            filtered = [t for t in filtered if t.is_overdue()]
        
        return filtered
    
    def get_overdue_tasks(self) -> List[Task]:
        """Get all overdue tasks."""
        return [task for task in self.tasks if task.is_overdue()]
    
    def get_upcoming_tasks(self, days: int = 7) -> List[Task]:
        """Get tasks due within specified days."""
        today = date.today()
        return [task for task in self.tasks 
                if task.due_date and not task.completed 
                and (task.due_date - today).days <= days
                and (task.due_date - today).days >= 0]
    
    def export_tasks(self, filename: str, tasks: Optional[List[Task]] = None) -> bool:
        """Export tasks to CSV."""
        if tasks is None:
            tasks = self.tasks
        return self.storage.export_to_csv(tasks, filename)
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get task statistics."""
        return self.storage.get_statistics(self.tasks)
    
    def _save(self):
        """Save tasks to storage."""
        self.storage.save_tasks(self.tasks)
    
    def __len__(self) -> int:
        """Return number of tasks."""
        return len(self.tasks)

Step 4: Build Command-Line Interface

Create the CLI that users will interact with.

# taskmaster/cli.py
import argparse
import sys
from datetime import date, datetime
from typing import List
from .task import Task
from .task_manager import TaskManager
from .storage import TaskStorage

class TaskCLI:
    """Command-line interface for TaskMaster."""
    
    def __init__(self):
        self.storage = TaskStorage()
        self.manager = TaskManager(self.storage)
    
    def run(self):
        """Main CLI entry point."""
        parser = self._create_parser()
        args = parser.parse_args()
        
        if not hasattr(args, 'command') or args.command is None:
            parser.print_help()
            return
        
        # Execute the appropriate command
        command_method = getattr(self, f"do_{args.command}", None)
        if command_method:
            try:
                command_method(args)
            except Exception as e:
                print(f"Error: {e}")
                sys.exit(1)
        else:
            print(f"Unknown command: {args.command}")
            sys.exit(1)
    
    def _create_parser(self) -> argparse.ArgumentParser:
        """Create the argument parser."""
        parser = argparse.ArgumentParser(
            prog='taskmaster',
            description='Task Manager CLI - Organize your tasks efficiently'
        )
        
        subparsers = parser.add_subparsers(dest='command', help='Available commands')
        
        # Add task command
        add_parser = subparsers.add_parser('add', help='Add a new task')
        add_parser.add_argument('title', help='Task title')
        add_parser.add_argument('-d', '--description', default='', help='Task description')
        add_parser.add_argument('-c', '--category', default='Other', 
                               choices=Task.CATEGORIES, help='Task category')
        add_parser.add_argument('-p', '--priority', default='Medium',
                               choices=Task.PRIORITIES, help='Task priority')
        add_parser.add_argument('--due-date', type=self._parse_date, 
                               help='Due date (YYYY-MM-DD)')
        
        # List tasks command
        list_parser = subparsers.add_parser('list', help='List tasks')
        list_parser.add_argument('--category', choices=Task.CATEGORIES, help='Filter by category')
        list_parser.add_argument('--priority', choices=Task.PRIORITIES, help='Filter by priority')
        list_parser.add_argument('--completed', action='store_true', help='Show only completed tasks')
        list_parser.add_argument('--pending', action='store_true', help='Show only pending tasks')
        list_parser.add_argument('--overdue', action='store_true', help='Show only overdue tasks')
        list_parser.add_argument('--sort', choices=['created_at', 'due_date', 'priority', 'title'],
                                default='created_at', help='Sort tasks by')
        list_parser.add_argument('--reverse', action='store_true', help='Reverse sort order')
        
        # Show task command
        show_parser = subparsers.add_parser('show', help='Show task details')
        show_parser.add_argument('task_id', type=int, help='Task ID')
        
        # Update task command
        update_parser = subparsers.add_parser('update', help='Update a task')
        update_parser.add_argument('task_id', type=int, help='Task ID')
        update_parser.add_argument('-t', '--title', help='New title')
        update_parser.add_argument('-d', '--description', help='New description')
        update_parser.add_argument('-c', '--category', choices=Task.CATEGORIES, help='New category')
        update_parser.add_argument('-p', '--priority', choices=Task.PRIORITIES, help='New priority')
        update_parser.add_argument('--due-date', type=self._parse_date, help='New due date')
        
        # Complete/Incomplete commands
        complete_parser = subparsers.add_parser('complete', help='Mark task as complete')
        complete_parser.add_argument('task_id', type=int, help='Task ID')
        
        incomplete_parser = subparsers.add_parser('incomplete', help='Mark task as incomplete')
        incomplete_parser.add_argument('task_id', type=int, help='Task ID')
        
        # Delete command
        delete_parser = subparsers.add_parser('delete', help='Delete a task')
        delete_parser.add_argument('task_id', type=int, help='Task ID')
        delete_parser.add_argument('--force', action='store_true', help='Skip confirmation')
        
        # Search command
        search_parser = subparsers.add_parser('search', help='Search tasks')
        search_parser.add_argument('query', help='Search query')
        
        # Statistics command
        subparsers.add_parser('stats', help='Show task statistics')
        
        # Export command
        export_parser = subparsers.add_parser('export', help='Export tasks to CSV')
        export_parser.add_argument('filename', help='Output filename')
        export_parser.add_argument('--completed-only', action='store_true', 
                                  help='Export only completed tasks')
        
        return parser
    
    def _parse_date(self, date_str: str) -> date:
        """Parse date string into date object."""
        try:
            return date.fromisoformat(date_str)
        except ValueError:
            raise argparse.ArgumentTypeError(f"Invalid date format: {date_str}. Use YYYY-MM-DD")
    
    def do_add(self, args):
        """Add a new task."""
        task = self.manager.add_task(
            title=args.title,
            description=args.description,
            category=args.category,
            priority=args.priority,
            due_date=args.due_date
        )
        print(f"βœ“ Task added successfully!")
        print(f"  ID: {task.task_id}")
        print(f"  Title: {task.title}")
        if task.due_date:
            print(f"  Due: {task.due_date}")
    
    def do_list(self, args):
        """List tasks with optional filtering."""
        # Determine filter criteria
        completed = None
        if args.completed:
            completed = True
        elif args.pending:
            completed = False
        
        # Get filtered tasks
        tasks = self.manager.filter_tasks(
            category=args.category,
            priority=args.priority,
            completed=completed,
            overdue=args.overdue
        )
        
        # Sort tasks
        if args.sort == "due_date":
            tasks.sort(key=lambda t: (t.due_date is None, t.due_date), reverse=args.reverse)
        elif args.sort == "priority":
            priority_order = {'High': 3, 'Medium': 2, 'Low': 1}
            tasks.sort(key=lambda t: priority_order.get(t.priority, 0), reverse=args.reverse)
        elif args.sort == "title":
            tasks.sort(key=lambda t: t.title.lower(), reverse=args.reverse)
        else:  # created_at
            tasks.sort(key=lambda t: t.created_at, reverse=args.reverse)
        
        if not tasks:
            print("No tasks found.")
            return
        
        print(f"πŸ“‹ Tasks ({len(tasks)} found):")
        print("-" * 80)
        
        for task in tasks:
            print(f"{task.task_id:6d} | {str(task)}")
        
        print("-" * 80)
    
    def do_show(self, args):
        """Show detailed task information."""
        task = self.manager.get_task(args.task_id)
        if not task:
            print(f"Task {args.task_id} not found.")
            return
        
        print(f"πŸ“‹ Task Details (ID: {task.task_id})")
        print("=" * 40)
        print(f"Title:       {task.title}")
        print(f"Description: {task.description or '(no description)'}")
        print(f"Category:    {task.category}")
        print(f"Priority:    {task.priority}")
        print(f"Status:      {'Completed' if task.completed else 'Pending'}")
        print(f"Due Date:    {task.due_date or 'Not set'}")
        
        if task.due_date and not task.completed:
            days = task.days_until_due()
            if days == 0:
                print("Due Status:  DUE TODAY")
            elif days > 0:
                print(f"Due Status:  {days} days remaining")
            else:
                print(f"Due Status:  OVERDUE by {abs(days)} days")
        
        print(f"Created:     {task.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
        print(f"Updated:     {task.updated_at.strftime('%Y-%m-%d %H:%M:%S')}")
    
    def do_update(self, args):
        """Update a task."""
        # Check if any updates were provided
        updates = {}
        if args.title:
            updates['title'] = args.title
        if args.description:
            updates['description'] = args.description
        if args.category:
            updates['category'] = args.category
        if args.priority:
            updates['priority'] = args.priority
        if args.due_date:
            updates['due_date'] = args.due_date
        
        if not updates:
            print("No updates specified. Use --help to see available options.")
            return
        
        if self.manager.update_task(args.task_id, **updates):
            print(f"βœ“ Task {args.task_id} updated successfully!")
        else:
            print(f"Task {args.task_id} not found.")
    
    def do_complete(self, args):
        """Mark task as complete."""
        if self.manager.mark_complete(args.task_id):
            print(f"βœ“ Task {args.task_id} marked as complete!")
        else:
            print(f"Task {args.task_id} not found.")
    
    def do_incomplete(self, args):
        """Mark task as incomplete."""
        if self.manager.mark_incomplete(args.task_id):
            print(f"βœ“ Task {args.task_id} marked as incomplete!")
        else:
            print(f"Task {args.task_id} not found.")
    
    def do_delete(self, args):
        """Delete a task."""
        task = self.manager.get_task(args.task_id)
        if not task:
            print(f"Task {args.task_id} not found.")
            return
        
        # Show confirmation unless --force is used
        if not args.force:
            print(f"Are you sure you want to delete task '{task.title}'? (y/N): ", end='')
            response = input().strip().lower()
            if response not in ['y', 'yes']:
                print("Deletion cancelled.")
                return
        
        if self.manager.delete_task(args.task_id):
            print(f"βœ“ Task {args.task_id} deleted successfully!")
        else:
            print(f"Failed to delete task {args.task_id}.")
    
    def do_search(self, args):
        """Search tasks."""
        tasks = self.manager.search_tasks(args.query)
        
        if not tasks:
            print(f"No tasks found matching '{args.query}'.")
            return
        
        print(f"πŸ” Search Results for '{args.query}' ({len(tasks)} found):")
        print("-" * 80)
        
        for task in tasks:
            print(f"{task.task_id:6d} | {str(task)}")
        
        print("-" * 80)
    
    def do_stats(self, args):
        """Show task statistics."""
        stats = self.manager.get_statistics()
        
        print("πŸ“Š Task Statistics")
        print("=" * 30)
        print(f"Total Tasks:     {stats['total_tasks']}")
        print(f"Completed:       {stats['completed_tasks']}")
        print(f"Pending:         {stats['pending_tasks']}")
        print(f"Overdue:         {stats['overdue_tasks']}")
        print(".1f")
        
        if stats['categories']:
            print("\nπŸ“‚ Tasks by Category:")
            for category, count in stats['categories'].items():
                print(f"  {category:12s}: {count}")
        
        if stats['priorities']:
            print("\n🚨 Tasks by Priority:")
            for priority, count in stats['priorities'].items():
                print(f"  {priority:8s}: {count}")
    
    def do_export(self, args):
        """Export tasks to CSV."""
        tasks = self.manager.tasks
        if args.completed_only:
            tasks = [t for t in tasks if t.completed]
        
        if self.manager.export_tasks(args.filename, tasks):
            print(f"βœ“ Tasks exported to {args.filename}")
        else:
            print("Failed to export tasks.")

def main():
    """Main entry point."""
    cli = TaskCLI()
    cli.run()

if __name__ == "__main__":
    main()

Step 5: Create Main Entry Point

# main.py
#!/usr/bin/env python3
"""
TaskMaster CLI - A command-line task management application.

Usage:
    python main.py <command> [options]

Commands:
    add      Add a new task
    list     List tasks
    show     Show task details
    update   Update a task
    complete Mark task as complete
    delete   Delete a task
    search   Search tasks
    stats    Show statistics
    export   Export tasks to CSV

Run 'python main.py <command> --help' for command-specific options.
"""

from taskmaster.cli import main

if __name__ == "__main__":
    main()

Step 6: Add Package Configuration

# setup.py
from setuptools import setup, find_packages

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name="taskmaster-cli",
    version="1.0.0",
    author="Your Name",
    author_email="your.email@example.com",
    description="A command-line task management application",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/yourusername/taskmaster-cli",
    packages=find_packages(),
    classifiers=[
        "Development Status :: 4 - Beta",
        "Intended Audience :: End Users/Desktop",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.8",
        "Programming Language :: Python :: 3.9",
        "Programming Language :: Python :: 3.10",
    ],
    python_requires=">=3.8",
    entry_points={
        "console_scripts": [
            "taskmaster=main:main",
        ],
    },
    include_package_data=True,
    zip_safe=False,
)
# requirements.txt
# TaskMaster CLI Requirements
# taskmaster/__init__.py
"""
TaskMaster CLI - A command-line task management application.
"""

__version__ = "1.0.0"
__author__ = "Your Name"
__email__ = "your.email@example.com"

from .task import Task
from .task_manager import TaskManager
from .storage import TaskStorage
from .cli import TaskCLI

__all__ = ["Task", "TaskManager", "TaskStorage", "TaskCLI"]

Step 7: Write Tests

# tests/test_task.py
import unittest
from datetime import date
from taskmaster.task import Task

class TestTask(unittest.TestCase):
    """Test cases for Task class."""
    
    def test_task_creation(self):
        """Test basic task creation."""
        task = Task("Test Task", "Test description", "Work", "High")
        self.assertEqual(task.title, "Test Task")
        self.assertEqual(task.description, "Test description")
        self.assertEqual(task.category, "Work")
        self.assertEqual(task.priority, "High")
        self.assertFalse(task.completed)
    
    def test_task_with_due_date(self):
        """Test task with due date."""
        due_date = date(2024, 12, 31)
        task = Task("Future Task", due_date=due_date)
        self.assertEqual(task.due_date, due_date)
    
    def test_task_validation(self):
        """Test task validation."""
        with self.assertRaises(ValueError):
            Task("Invalid", category="InvalidCategory")
        
        with self.assertRaises(ValueError):
            Task("Invalid", priority="InvalidPriority")
    
    def test_task_completion(self):
        """Test task completion methods."""
        task = Task("Test Task")
        self.assertFalse(task.completed)
        
        task.mark_complete()
        self.assertTrue(task.completed)
        
        task.mark_incomplete()
        self.assertFalse(task.completed)
    
    def test_task_update(self):
        """Test task update functionality."""
        task = Task("Original Title")
        task.update(title="Updated Title", priority="High")
        self.assertEqual(task.title, "Updated Title")
        self.assertEqual(task.priority, "High")
    
    def test_overdue_detection(self):
        """Test overdue task detection."""
        past_date = date(2020, 1, 1)
        task = Task("Overdue Task", due_date=past_date)
        self.assertTrue(task.is_overdue())
        
        future_date = date(2030, 1, 1)
        task2 = Task("Future Task", due_date=future_date)
        self.assertFalse(task2.is_overdue())
    
    def test_serialization(self):
        """Test task serialization."""
        task = Task("Test Task", "Description", "Work", "High", date(2024, 12, 31))
        task_dict = task.to_dict()
        
        # Test deserialization
        restored_task = Task.from_dict(task_dict)
        self.assertEqual(restored_task.title, task.title)
        self.assertEqual(restored_task.due_date, task.due_date)

if __name__ == '__main__':
    unittest.main()

Step 8: Create Documentation

# TaskMaster CLI

A powerful command-line task management application built with Python.

## Features

- βœ… Create, read, update, and delete tasks
- πŸ“‚ Organize tasks by categories and priorities
- πŸ“… Set due dates with overdue detection
- πŸ” Search and filter tasks
- πŸ’Ύ Persistent JSON storage
- πŸ“Š Export tasks to CSV
- πŸ“ˆ Task completion statistics

## Installation

### From Source
```bash
git clone https://github.com/yourusername/taskmaster-cli.git
cd taskmaster-cli
pip install -r requirements.txt
python setup.py install

As Package

pip install taskmaster-cli

Usage

Add a Task

# Basic task
taskmaster add "Buy groceries"

# Task with details
taskmaster add "Finish project report" -d "Complete the quarterly report" -c Work -p High --due-date 2024-12-31

List Tasks

# All tasks
taskmaster list

# Filter by category
taskmaster list --category Work

# Show only pending tasks
taskmaster list --pending

# Sort by due date
taskmaster list --sort due_date

Manage Tasks

# Show task details
taskmaster show 123456

# Update task
taskmaster update 123456 -t "New title" -p High

# Mark complete/incomplete
taskmaster complete 123456
taskmaster incomplete 123456

# Delete task
taskmaster delete 123456

Search and Export

# Search tasks
taskmaster search "project"

# Show statistics
taskmaster stats

# Export to CSV
taskmaster export tasks.csv
taskmaster export completed_tasks.csv --completed-only

Task Categories

  • Work - Professional tasks
  • Personal - Personal errands and activities
  • Shopping - Shopping and purchases
  • Health - Health and fitness related
  • Education - Learning and courses
  • Other - Miscellaneous tasks

Task Priorities

  • High - Urgent and important
  • Medium - Important but not urgent
  • Low - Nice to have

Data Storage

Tasks are stored in JSON format in the data/tasks.json file. The application automatically creates this file and directory structure on first run.

Development

Running Tests

python -m pytest tests/

Code Quality

# Format code
black taskmaster/

# Lint code
flake8 taskmaster/

# Type checking
mypy taskmaster/

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License

MIT License - see LICENSE file for details.

Support

For issues and questions:


## Step 9: Test the Application

Let's create a simple test script to verify everything works:

```python
# test_app.py
#!/usr/bin/env python3
"""
Test script for TaskMaster CLI application.
"""

import os
import sys
import subprocess
from datetime import date, timedelta

def run_command(cmd):
    """Run a command and return the result."""
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
        return result.returncode, result.stdout, result.stderr
    except Exception as e:
        return 1, "", str(e)

def test_basic_functionality():
    """Test basic application functionality."""
    print("πŸ§ͺ Testing TaskMaster CLI...")
    
    # Clean up any existing data
    if os.path.exists("data/tasks.json"):
        os.remove("data/tasks.json")
    
    # Test adding a task
    print("\n1. Testing task addition...")
    returncode, stdout, stderr = run_command('python main.py add "Test Task" -d "Test description" -c Work -p High')
    if returncode != 0:
        print(f"❌ Failed to add task: {stderr}")
        return False
    print("βœ… Task added successfully")
    
    # Test listing tasks
    print("\n2. Testing task listing...")
    returncode, stdout, stderr = run_command('python main.py list')
    if returncode != 0 or "Test Task" not in stdout:
        print(f"❌ Failed to list tasks: {stderr}")
        return False
    print("βœ… Task listing works")
    
    # Test task completion
    print("\n3. Testing task completion...")
    # First get task ID from list output
    lines = stdout.split('\n')
    task_line = [line for line in lines if "Test Task" in line][0]
    task_id = task_line.split('|')[0].strip()
    
    returncode, stdout, stderr = run_command(f'python main.py complete {task_id}')
    if returncode != 0:
        print(f"❌ Failed to complete task: {stderr}")
        return False
    print("βœ… Task completion works")
    
    # Test statistics
    print("\n4. Testing statistics...")
    returncode, stdout, stderr = run_command('python main.py stats')
    if returncode != 0 or "Total Tasks:" not in stdout:
        print(f"❌ Failed to show statistics: {stderr}")
        return False
    print("βœ… Statistics work")
    
    # Test export
    print("\n5. Testing export...")
    returncode, stdout, stderr = run_command('python main.py export test_tasks.csv')
    if returncode != 0 or not os.path.exists("data/test_tasks.csv"):
        print(f"❌ Failed to export tasks: {stderr}")
        return False
    print("βœ… Export works")
    
    # Clean up
    if os.path.exists("data/test_tasks.csv"):
        os.remove("data/test_tasks.csv")
    
    print("\nπŸŽ‰ All tests passed!")
    return True

if __name__ == "__main__":
    success = test_basic_functionality()
    sys.exit(0 if success else 1)

Usage Examples

Daily Workflow

# Start your day by checking tasks
taskmaster list --pending --sort due_date

# Add new tasks as they come up
taskmaster add "Call dentist" -c Health -p High --due-date 2024-12-15
taskmaster add "Review code changes" -c Work -p Medium

# Complete tasks throughout the day
taskmaster complete 123456

# Check overdue tasks
taskmaster list --overdue

# End of day review
taskmaster stats

Project Management

# Add project tasks
taskmaster add "Design database schema" -c Work -p High --due-date 2024-12-20
taskmaster add "Implement user authentication" -c Work -p High --due-date 2024-12-25
taskmaster add "Write unit tests" -c Work -p Medium --due-date 2024-12-28

# Track progress
taskmaster list --category Work --pending
taskmaster complete 123456  # Mark design as complete

# Export for reporting
taskmaster export project_tasks.csv --completed-only

Personal Organization

# Weekly planning
taskmaster add "Grocery shopping" -c Shopping -p Medium --due-date 2024-12-16
taskmaster add "Gym workout" -c Health -p Medium --due-date 2024-12-14
taskmaster add "Read technical book" -c Education -p Low

# Daily review
taskmaster list --due-date --reverse  # Show tasks due soon first

# Weekend cleanup
taskmaster search "book"  # Find reading tasks
taskmaster stats  # Check productivity

Advanced Features to Consider

Once you have the basic application working, consider adding:

  1. Recurring Tasks - Tasks that repeat daily/weekly/monthly
  2. Task Templates - Pre-defined task structures
  3. Time Tracking - Track time spent on tasks
  4. Notifications - Email/SMS reminders for due tasks
  5. Collaboration - Share tasks with team members
  6. Mobile App - Companion mobile application
  7. Web Interface - Browser-based task management
  8. Data Visualization - Charts and graphs for productivity

Summary

TaskMaster CLI demonstrates professional Python development:

Core Concepts:

  • Object-oriented design with classes and inheritance
  • Data persistence with JSON serialization
  • Command-line interface with argparse
  • Error handling and validation
  • Modular code organization

Key Skills:

  • Problem decomposition and planning
  • Clean code principles
  • Testing and debugging
  • Documentation and user experience
  • Version control and project structure

Next Steps:

  1. Implement the basic functionality
  2. Add comprehensive tests
  3. Create user documentation
  4. Package for distribution
  5. Deploy and share with others

Congratulations! You’ve built your first complete Python application! πŸŽ‰

Ready for the next project? Let’s build a Weather Dashboard! 🌀️