Build a comprehensive task management application integrating all Python concepts: OOP, file handling, databases, APIs, testing, and deployment.
This capstone lesson brings together everything you've learned in the Python Fundamentals course. You'll build a complete, production-ready task management application that demonstrates professional Python development practices.
The project integrates object-oriented programming, file operations, database management, API development, error handling, testing, and documentationโall the skills needed for enterprise Python development. Think of this as your portfolio piece that showcases your Python expertise to potential employers.
Companies like Atlassian (Jira), Asana, and Trello have built multi-billion dollar businesses around task management systems. By the end of this lesson, you'll understand the architecture and implementation details behind such applications.
A well-architected Python application follows established design patterns and maintains separation of concerns for scalability and maintainability.
Model-View-Controller (MVC) Architecture:
1# Project Structure
2task_manager/
3โโโ models/ # Data models and database interactions
4โ โโโ __init__.py
5โ โโโ task.py # Task model
6โ โโโ user.py # User model
7โ โโโ database.py # Database connection and utilities
8โโโ controllers/ # Business logic
9โ โโโ __init__.py
10โ โโโ task_controller.py
11โ โโโ user_controller.py
12โโโ views/ # User interface (CLI or web)
13โ โโโ __init__.py
14โ โโโ cli.py # Command line interface
15โ โโโ api.py # REST API endpoints
16โโโ utils/ # Utility functions
17โ โโโ __init__.py
18โ โโโ validators.py
19โ โโโ helpers.py
20โ โโโ config.py
21โโโ tests/ # Test suites
22โ โโโ __init__.py
23โ โโโ test_models.py
24โ โโโ test_controllers.py
25โ โโโ test_integration.py
26โโโ docs/ # Documentation
27โโโ requirements.txt
28โโโ setup.py
29โโโ main.py # Application entry pointCore Models Implementation:
1# models/task.py
2from datetime import datetime, date
3from enum import Enum
4from typing import Optional, List
5import uuid
6
7class Priority(Enum):
8 LOW = "low"
9 MEDIUM = "medium"
10 HIGH = "high"
11 URGENT = "urgent"
12
13class Status(Enum):
14 TODO = "todo"
15 IN_PROGRESS = "in_progress"
16 COMPLETED = "completed"
17 CANCELLED = "cancelled"
18
19class Task:
20 """Task model with comprehensive functionality"""
21
22 def __init__(self, title: str, description: str = "",
23 priority: Priority = Priority.MEDIUM,
24 due_date: Optional[date] = None,
25 assignee: Optional[str] = None):
26 self.id = str(uuid.uuid4())
27 self.title = title
28 self.description = description
29 self.priority = priority
30 self.status = Status.TODO
31 self.due_date = due_date
32 self.assignee = assignee
33 self.created_at = datetime.now()
34 self.updated_at = datetime.now()
35 self.completed_at: Optional[datetime] = None
36 self.tags: List[str] = []
37 self.subtasks: List['Task'] = []
38
39 def mark_completed(self) -> None:
40 """Mark task as completed"""
41 self.status = Status.COMPLETED
42 self.completed_at = datetime.now()
43 self.updated_at = datetime.now()
44
45 def add_subtask(self, subtask: 'Task') -> None:
46 """Add a subtask"""
47 self.subtasks.append(subtask)
48 self.updated_at = datetime.now()
49
50 def add_tag(self, tag: str) -> None:
51 """Add a tag to the task"""
52 if tag not in self.tags:
53 self.tags.append(tag)
54 self.updated_at = datetime.now()
55
56 def is_overdue(self) -> bool:
57 """Check if task is overdue"""
58 if self.due_date and self.status != Status.COMPLETED:
59 return date.today() > self.due_date
60 return False
61
62 def get_progress_percentage(self) -> float:
63 """Calculate completion percentage including subtasks"""
64 if not self.subtasks:
65 return 100.0 if self.status == Status.COMPLETED else 0.0
66
67 completed_subtasks = sum(1 for subtask in self.subtasks
68 if subtask.status == Status.COMPLETED)
69 return (completed_subtasks / len(self.subtasks)) * 100
70
71 def to_dict(self) -> dict:
72 """Convert task to dictionary for serialization"""
73 return {
74 'id': self.id,
75 'title': self.title,
76 'description': self.description,
77 'priority': self.priority.value,
78 'status': self.status.value,
79 'due_date': self.due_date.isoformat() if self.due_date else None,
80 'assignee': self.assignee,
81 'created_at': self.created_at.isoformat(),
82 'updated_at': self.updated_at.isoformat(),
83 'completed_at': self.completed_at.isoformat() if self.completed_at else None,
84 'tags': self.tags,
85 'progress': self.get_progress_percentage(),
86 'is_overdue': self.is_overdue()
87 }
88
89 @classmethod
90 def from_dict(cls, data: dict) -> 'Task':
91 """Create task from dictionary"""
92 task = cls(
93 title=data['title'],
94 description=data.get('description', ''),
95 priority=Priority(data.get('priority', Priority.MEDIUM.value)),
96 due_date=date.fromisoformat(data['due_date']) if data.get('due_date') else None,
97 assignee=data.get('assignee')
98 )
99 task.id = data['id']
100 task.status = Status(data.get('status', Status.TODO.value))
101 task.created_at = datetime.fromisoformat(data['created_at'])
102 task.updated_at = datetime.fromisoformat(data['updated_at'])
103 if data.get('completed_at'):
104 task.completed_at = datetime.fromisoformat(data['completed_at'])
105 task.tags = data.get('tags', [])
106 return task
107
108 def __repr__(self) -> str:
109 return f"Task(id='{self.id[:8]}', title='{self.title}', status='{self.status.value}')"
110
111 def __str__(self) -> str:
112 status_icon = "โ
" if self.status == Status.COMPLETED else "โณ" if self.status == Status.IN_PROGRESS else "๐"
113 priority_icon = {"urgent": "๐ด", "high": "๐ก", "medium": "๐ข", "low": "โช"}.get(self.priority.value, "๐ข")
114
115 due_info = f" (Due: {self.due_date})" if self.due_date else ""
116 overdue_flag = " โ ๏ธ OVERDUE" if self.is_overdue() else ""
117
118 return f"{status_icon} {priority_icon} {self.title}{due_info}{overdue_flag}"User Management:
1# models/user.py
2from datetime import datetime
3from typing import List, Optional
4import hashlib
5import uuid
6
7class User:
8 """User model with authentication and profile management"""
9
10 def __init__(self, username: str, email: str, password: str,
11 full_name: Optional[str] = None):
12 self.id = str(uuid.uuid4())
13 self.username = username
14 self.email = email
15 self.full_name = full_name or username
16 self.password_hash = self._hash_password(password)
17 self.created_at = datetime.now()
18 self.last_login: Optional[datetime] = None
19 self.is_active = True
20 self.role = "user" # user, admin, manager
21
22 def _hash_password(self, password: str) -> str:
23 """Hash password with salt"""
24 salt = "task_manager_salt_2024" # In production, use random salt per user
25 return hashlib.sha256(f"{password}{salt}".encode()).hexdigest()
26
27 def verify_password(self, password: str) -> bool:
28 """Verify password against hash"""
29 return self._hash_password(password) == self.password_hash
30
31 def update_last_login(self) -> None:
32 """Update last login timestamp"""
33 self.last_login = datetime.now()
34
35 def to_dict(self) -> dict:
36 """Convert user to dictionary (excluding sensitive data)"""
37 return {
38 'id': self.id,
39 'username': self.username,
40 'email': self.email,
41 'full_name': self.full_name,
42 'created_at': self.created_at.isoformat(),
43 'last_login': self.last_login.isoformat() if self.last_login else None,
44 'is_active': self.is_active,
45 'role': self.role
46 }
47
48 def __repr__(self) -> str:
49 return f"User(username='{self.username}', email='{self.email}')"Database Layer:
1# models/database.py
2import json
3import os
4from datetime import datetime, date
5from typing import Dict, List, Optional, Any
6from pathlib import Path
7
8class JSONDatabase:
9 """Simple JSON-based database for demonstration"""
10
11 def __init__(self, db_path: str = "task_manager.json"):
12 self.db_path = Path(db_path)
13 self.data = self._load_data()
14
15 def _load_data(self) -> Dict[str, Any]:
16 """Load data from JSON file"""
17 if self.db_path.exists():
18 try:
19 with open(self.db_path, 'r', encoding='utf-8') as f:
20 return json.load(f)
21 except (json.JSONDecodeError, FileNotFoundError):
22 pass
23
24 # Return default structure
25 return {
26 'tasks': [],
27 'users': [],
28 'metadata': {
29 'version': '1.0',
30 'created_at': datetime.now().isoformat(),
31 'last_backup': None
32 }
33 }
34
35 def save(self) -> bool:
36 """Save data to JSON file"""
37 try:
38 # Create backup before saving
39 if self.db_path.exists():
40 backup_path = self.db_path.with_suffix('.backup.json')
41 self.db_path.replace(backup_path)
42
43 with open(self.db_path, 'w', encoding='utf-8') as f:
44 json.dump(self.data, f, indent=2, ensure_ascii=False)
45
46 self.data['metadata']['last_backup'] = datetime.now().isoformat()
47 return True
48
49 except Exception as e:
50 print(f"Error saving database: {e}")
51 return False
52
53 def add_task(self, task_dict: dict) -> bool:
54 """Add task to database"""
55 self.data['tasks'].append(task_dict)
56 return self.save()
57
58 def get_tasks(self, user_id: Optional[str] = None) -> List[dict]:
59 """Get tasks, optionally filtered by user"""
60 tasks = self.data['tasks']
61 if user_id:
62 tasks = [task for task in tasks if task.get('assignee') == user_id]
63 return tasks
64
65 def update_task(self, task_id: str, updates: dict) -> bool:
66 """Update task by ID"""
67 for task in self.data['tasks']:
68 if task['id'] == task_id:
69 task.update(updates)
70 task['updated_at'] = datetime.now().isoformat()
71 return self.save()
72 return False
73
74 def delete_task(self, task_id: str) -> bool:
75 """Delete task by ID"""
76 original_length = len(self.data['tasks'])
77 self.data['tasks'] = [task for task in self.data['tasks']
78 if task['id'] != task_id]
79
80 if len(self.data['tasks']) < original_length:
81 return self.save()
82 return False
83
84 def add_user(self, user_dict: dict) -> bool:
85 """Add user to database"""
86 self.data['users'].append(user_dict)
87 return self.save()
88
89 def get_user(self, username: str) -> Optional[dict]:
90 """Get user by username"""
91 for user in self.data['users']:
92 if user['username'] == username:
93 return user
94 return None
95
96 def get_statistics(self) -> dict:
97 """Get database statistics"""
98 tasks = self.data['tasks']
99 users = self.data['users']
100
101 completed_tasks = sum(1 for task in tasks if task['status'] == 'completed')
102 overdue_tasks = sum(1 for task in tasks if task.get('is_overdue', False))
103
104 return {
105 'total_tasks': len(tasks),
106 'completed_tasks': completed_tasks,
107 'overdue_tasks': overdue_tasks,
108 'completion_rate': (completed_tasks / len(tasks) * 100) if tasks else 0,
109 'total_users': len(users),
110 'database_size': self.db_path.stat().st_size if self.db_path.exists() else 0
111 }Design Pattern Benefits:
Controllers handle the business logic, coordinate between models and views, and implement the core application functionality.
Task Controller Implementation:
1# controllers/task_controller.py
2from datetime import date, datetime
3from typing import List, Optional, Dict, Any
4from models.task import Task, Priority, Status
5from models.database import JSONDatabase
6from utils.validators import validate_task_data
7from utils.helpers import parse_date, format_output
8
9class TaskController:
10 """Business logic for task management"""
11
12 def __init__(self, database: Optional[JSONDatabase] = None):
13 self.db = database or JSONDatabase()
14 self.current_user_id: Optional[str] = None
15
16 def set_current_user(self, user_id: str) -> None:
17 """Set the current authenticated user"""
18 self.current_user_id = user_id
19
20 def create_task(self, title: str, description: str = "",
21 priority: str = "medium", due_date: Optional[str] = None,
22 tags: Optional[List[str]] = None) -> Dict[str, Any]:
23 """Create a new task"""
24 try:
25 # Validate input data
26 validation_result = validate_task_data({
27 'title': title,
28 'description': description,
29 'priority': priority,
30 'due_date': due_date
31 })
32
33 if not validation_result['is_valid']:
34 return {
35 'success': False,
36 'error': validation_result['errors'],
37 'task': None
38 }
39
40 # Parse due date if provided
41 parsed_due_date = parse_date(due_date) if due_date else None
42
43 # Create task instance
44 task = Task(
45 title=title,
46 description=description,
47 priority=Priority(priority),
48 due_date=parsed_due_date,
49 assignee=self.current_user_id
50 )
51
52 # Add tags if provided
53 if tags:
54 for tag in tags:
55 task.add_tag(tag.strip().lower())
56
57 # Save to database
58 if self.db.add_task(task.to_dict()):
59 return {
60 'success': True,
61 'message': f"Task '{title}' created successfully",
62 'task': task.to_dict()
63 }
64 else:
65 return {
66 'success': False,
67 'error': "Failed to save task to database",
68 'task': None
69 }
70
71 except Exception as e:
72 return {
73 'success': False,
74 'error': f"Error creating task: {str(e)}",
75 'task': None
76 }
77
78 def get_tasks(self, status: Optional[str] = None,
79 priority: Optional[str] = None,
80 tag: Optional[str] = None,
81 overdue_only: bool = False) -> Dict[str, Any]:
82 """Get tasks with optional filtering"""
83 try:
84 # Get all tasks for current user
85 all_tasks = self.db.get_tasks(self.current_user_id)
86
87 # Convert to Task objects for easier filtering
88 task_objects = [Task.from_dict(task_data) for task_data in all_tasks]
89
90 # Apply filters
91 filtered_tasks = task_objects
92
93 if status:
94 filtered_tasks = [t for t in filtered_tasks
95 if t.status.value == status]
96
97 if priority:
98 filtered_tasks = [t for t in filtered_tasks
99 if t.priority.value == priority]
100
101 if tag:
102 filtered_tasks = [t for t in filtered_tasks
103 if tag.lower() in [t.lower() for t in t.tags]]
104
105 if overdue_only:
106 filtered_tasks = [t for t in filtered_tasks if t.is_overdue()]
107
108 # Sort by priority and due date
109 filtered_tasks.sort(key=lambda t: (
110 {'urgent': 0, 'high': 1, 'medium': 2, 'low': 3}[t.priority.value],
111 t.due_date or date.max
112 ))
113
114 return {
115 'success': True,
116 'tasks': [task.to_dict() for task in filtered_tasks],
117 'total_count': len(filtered_tasks),
118 'filters_applied': {
119 'status': status,
120 'priority': priority,
121 'tag': tag,
122 'overdue_only': overdue_only
123 }
124 }
125
126 except Exception as e:
127 return {
128 'success': False,
129 'error': f"Error retrieving tasks: {str(e)}",
130 'tasks': []
131 }
132
133 def update_task(self, task_id: str, **updates) -> Dict[str, Any]:
134 """Update task with provided fields"""
135 try:
136 # Get current task data
137 all_tasks = self.db.get_tasks(self.current_user_id)
138 current_task_data = None
139
140 for task_data in all_tasks:
141 if task_data['id'] == task_id:
142 current_task_data = task_data
143 break
144
145 if not current_task_data:
146 return {
147 'success': False,
148 'error': f"Task with ID {task_id} not found"
149 }
150
151 # Create task object for validation
152 task = Task.from_dict(current_task_data)
153
154 # Apply updates
155 update_dict = {}
156
157 if 'title' in updates:
158 task.title = updates['title']
159 update_dict['title'] = updates['title']
160
161 if 'description' in updates:
162 task.description = updates['description']
163 update_dict['description'] = updates['description']
164
165 if 'priority' in updates:
166 task.priority = Priority(updates['priority'])
167 update_dict['priority'] = updates['priority']
168
169 if 'status' in updates:
170 task.status = Status(updates['status'])
171 update_dict['status'] = updates['status']
172
173 # If marking as completed, set completion time
174 if updates['status'] == 'completed':
175 task.mark_completed()
176 update_dict['completed_at'] = task.completed_at.isoformat()
177
178 if 'due_date' in updates:
179 task.due_date = parse_date(updates['due_date']) if updates['due_date'] else None
180 update_dict['due_date'] = task.due_date.isoformat() if task.due_date else None
181
182 if 'add_tag' in updates:
183 task.add_tag(updates['add_tag'])
184 update_dict['tags'] = task.tags
185
186 # Update timestamp
187 task.updated_at = datetime.now()
188 update_dict['updated_at'] = task.updated_at.isoformat()
189
190 # Save to database
191 if self.db.update_task(task_id, update_dict):
192 return {
193 'success': True,
194 'message': f"Task '{task.title}' updated successfully",
195 'task': task.to_dict()
196 }
197 else:
198 return {
199 'success': False,
200 'error': "Failed to update task in database"
201 }
202
203 except Exception as e:
204 return {
205 'success': False,
206 'error': f"Error updating task: {str(e)}"
207 }
208
209 def delete_task(self, task_id: str) -> Dict[str, Any]:
210 """Delete a task"""
211 try:
212 # Verify task exists and belongs to user
213 all_tasks = self.db.get_tasks(self.current_user_id)
214 task_exists = any(task['id'] == task_id for task in all_tasks)
215
216 if not task_exists:
217 return {
218 'success': False,
219 'error': f"Task with ID {task_id} not found"
220 }
221
222 # Delete from database
223 if self.db.delete_task(task_id):
224 return {
225 'success': True,
226 'message': "Task deleted successfully"
227 }
228 else:
229 return {
230 'success': False,
231 'error': "Failed to delete task from database"
232 }
233
234 except Exception as e:
235 return {
236 'success': False,
237 'error': f"Error deleting task: {str(e)}"
238 }
239
240 def get_dashboard_summary(self) -> Dict[str, Any]:
241 """Get dashboard summary with statistics"""
242 try:
243 tasks_result = self.get_tasks()
244
245 if not tasks_result['success']:
246 return tasks_result
247
248 tasks = tasks_result['tasks']
249
250 # Calculate statistics
251 total_tasks = len(tasks)
252 completed_tasks = sum(1 for task in tasks if task['status'] == 'completed')
253 in_progress_tasks = sum(1 for task in tasks if task['status'] == 'in_progress')
254 overdue_tasks = sum(1 for task in tasks if task.get('is_overdue', False))
255
256 # Priority breakdown
257 priority_counts = {}
258 for priority in ['urgent', 'high', 'medium', 'low']:
259 priority_counts[priority] = sum(1 for task in tasks
260 if task['priority'] == priority)
261
262 # Recent activity (tasks created or updated in last 7 days)
263 recent_activity = []
264 seven_days_ago = datetime.now().timestamp() - (7 * 24 * 60 * 60)
265
266 for task in tasks:
267 updated_time = datetime.fromisoformat(task['updated_at']).timestamp()
268 if updated_time > seven_days_ago:
269 recent_activity.append({
270 'task_id': task['id'],
271 'title': task['title'],
272 'action': 'completed' if task['status'] == 'completed' else 'updated',
273 'timestamp': task['updated_at']
274 })
275
276 # Sort recent activity by timestamp
277 recent_activity.sort(key=lambda x: x['timestamp'], reverse=True)
278
279 completion_rate = (completed_tasks / total_tasks * 100) if total_tasks > 0 else 0
280
281 return {
282 'success': True,
283 'summary': {
284 'total_tasks': total_tasks,
285 'completed_tasks': completed_tasks,
286 'in_progress_tasks': in_progress_tasks,
287 'overdue_tasks': overdue_tasks,
288 'completion_rate': round(completion_rate, 1),
289 'priority_breakdown': priority_counts,
290 'recent_activity': recent_activity[:10] # Last 10 activities
291 }
292 }
293
294 except Exception as e:
295 return {
296 'success': False,
297 'error': f"Error generating dashboard summary: {str(e)}"
298 }Utility Functions:
1# utils/validators.py
2import re
3from datetime import datetime
4from typing import Dict, Any, List
5
6def validate_task_data(data: Dict[str, Any]) -> Dict[str, Any]:
7 """Validate task creation/update data"""
8 errors = []
9
10 # Title validation
11 if not data.get('title', '').strip():
12 errors.append("Title is required")
13 elif len(data['title'].strip()) > 200:
14 errors.append("Title must be less than 200 characters")
15
16 # Description validation
17 if data.get('description') and len(data['description']) > 1000:
18 errors.append("Description must be less than 1000 characters")
19
20 # Priority validation
21 valid_priorities = ['low', 'medium', 'high', 'urgent']
22 if data.get('priority') and data['priority'] not in valid_priorities:
23 errors.append(f"Priority must be one of: {', '.join(valid_priorities)}")
24
25 # Due date validation
26 if data.get('due_date'):
27 try:
28 datetime.fromisoformat(data['due_date'])
29 except ValueError:
30 errors.append("Due date must be in YYYY-MM-DD format")
31
32 return {
33 'is_valid': len(errors) == 0,
34 'errors': errors
35 }
36
37def validate_user_data(data: Dict[str, Any]) -> Dict[str, Any]:
38 """Validate user registration data"""
39 errors = []
40
41 # Username validation
42 username = data.get('username', '').strip()
43 if not username:
44 errors.append("Username is required")
45 elif len(username) < 3:
46 errors.append("Username must be at least 3 characters")
47 elif not re.match(r'^[a-zA-Z0-9_]+$', username):
48 errors.append("Username can only contain letters, numbers, and underscores")
49
50 # Email validation
51 email = data.get('email', '').strip()
52 if not email:
53 errors.append("Email is required")
54 elif not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
55 errors.append("Invalid email format")
56
57 # Password validation
58 password = data.get('password', '')
59 if not password:
60 errors.append("Password is required")
61 elif len(password) < 8:
62 errors.append("Password must be at least 8 characters")
63 elif not re.search(r'[A-Z]', password):
64 errors.append("Password must contain at least one uppercase letter")
65 elif not re.search(r'[a-z]', password):
66 errors.append("Password must contain at least one lowercase letter")
67 elif not re.search(r'\d', password):
68 errors.append("Password must contain at least one number")
69
70 return {
71 'is_valid': len(errors) == 0,
72 'errors': errors
73 }
74
75# utils/helpers.py
76from datetime import datetime, date
77from typing import Optional, Any, Dict
78
79def parse_date(date_string: str) -> Optional[date]:
80 """Parse date string in various formats"""
81 formats = ['%Y-%m-%d', '%m/%d/%Y', '%d-%m-%Y']
82
83 for fmt in formats:
84 try:
85 return datetime.strptime(date_string, fmt).date()
86 except ValueError:
87 continue
88
89 return None
90
91def format_output(data: Any, format_type: str = 'pretty') -> str:
92 """Format output for display"""
93 if format_type == 'json':
94 import json
95 return json.dumps(data, indent=2, default=str)
96
97 elif format_type == 'table' and isinstance(data, list):
98 if not data:
99 return "No data to display"
100
101 # Simple table formatting for lists of dictionaries
102 if all(isinstance(item, dict) for item in data):
103 headers = list(data[0].keys())
104
105 # Calculate column widths
106 widths = {}
107 for header in headers:
108 widths[header] = max(
109 len(str(header)),
110 max(len(str(item.get(header, ''))) for item in data)
111 )
112
113 # Create table
114 header_row = ' | '.join(str(header).ljust(widths[header]) for header in headers)
115 separator = '-+-'.join('-' * widths[header] for header in headers)
116
117 rows = []
118 for item in data:
119 row = ' | '.join(str(item.get(header, '')).ljust(widths[header]) for header in headers)
120 rows.append(row)
121
122 return '\n'.join([header_row, separator] + rows)
123
124 return str(data)
125
126def calculate_business_days(start_date: date, end_date: date) -> int:
127 """Calculate business days between two dates"""
128 if start_date > end_date:
129 start_date, end_date = end_date, start_date
130
131 business_days = 0
132 current_date = start_date
133
134 while current_date <= end_date:
135 if current_date.weekday() < 5: # Monday is 0, Sunday is 6
136 business_days += 1
137 current_date = date.fromordinal(current_date.toordinal() + 1)
138
139 return business_days
140
141def generate_report_header() -> str:
142 """Generate a professional report header"""
143 return f"""
144โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
145โ TASK MANAGEMENT SYSTEM โ
146โ ACTIVITY REPORT โ
147โ โ
148โ Generated: {datetime.now().strftime("%B %d, %Y at %I:%M %p")} โ
149โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
150"""Controller Benefits:
The user interface layer provides multiple ways to interact with the application, from command-line interfaces to REST APIs.
Command Line Interface:
1# views/cli.py
2import sys
3from typing import Optional, List
4from datetime import date
5from controllers.task_controller import TaskController
6from controllers.user_controller import UserController
7from models.database import JSONDatabase
8from utils.helpers import format_output, generate_report_header
9
10class TaskManagerCLI:
11 """Command-line interface for task management"""
12
13 def __init__(self):
14 self.db = JSONDatabase()
15 self.task_controller = TaskController(self.db)
16 self.user_controller = UserController(self.db)
17 self.current_user = None
18
19 def display_banner(self):
20 """Display application banner"""
21 banner = """
22 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
23 โ ๐ TASK MANAGER PRO โ
24 โ Professional Task Management โ
25 โ Python Edition v1.0 โ
26 โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
27 """
28 print(banner)
29
30 def authenticate_user(self) -> bool:
31 """Handle user authentication"""
32 print("\n๐ค User Authentication")
33 print("-" * 30)
34
35 while True:
36 choice = input("(L)ogin or (R)egister? (q to quit): ").lower().strip()
37
38 if choice == 'q':
39 return False
40 elif choice == 'l':
41 return self.login()
42 elif choice == 'r':
43 if self.register():
44 return self.login()
45 else:
46 print("โ Invalid choice. Please enter 'l', 'r', or 'q'")
47
48 def login(self) -> bool:
49 """Handle user login"""
50 print("\n๐ Login")
51 username = input("Username: ").strip()
52 password = input("Password: ").strip()
53
54 result = self.user_controller.authenticate_user(username, password)
55
56 if result['success']:
57 self.current_user = result['user']
58 self.task_controller.set_current_user(self.current_user['id'])
59 print(f"โ
Welcome back, {self.current_user['full_name']}!")
60 return True
61 else:
62 print(f"โ Login failed: {result['error']}")
63 return False
64
65 def register(self) -> bool:
66 """Handle user registration"""
67 print("\n๐ Register New Account")
68
69 username = input("Username: ").strip()
70 email = input("Email: ").strip()
71 full_name = input("Full Name: ").strip()
72 password = input("Password: ").strip()
73 confirm_password = input("Confirm Password: ").strip()
74
75 if password != confirm_password:
76 print("โ Passwords do not match")
77 return False
78
79 result = self.user_controller.register_user(username, email, password, full_name)
80
81 if result['success']:
82 print("โ
Account created successfully! Please login.")
83 return True
84 else:
85 print(f"โ Registration failed:")
86 for error in result['errors']:
87 print(f" โข {error}")
88 return False
89
90 def display_main_menu(self):
91 """Display main application menu"""
92 print(f"\n๐ Main Menu - Welcome, {self.current_user['full_name']}")
93 print("-" * 50)
94 print("1. ๐ View Tasks")
95 print("2. โ Create New Task")
96 print("3. โ๏ธ Update Task")
97 print("4. โ Delete Task")
98 print("5. ๐ Dashboard Summary")
99 print("6. ๐ Search Tasks")
100 print("7. ๐ Generate Report")
101 print("8. โ๏ธ Settings")
102 print("9. ๐ช Logout")
103 print("0. ๐ Exit")
104
105 def handle_view_tasks(self):
106 """Handle viewing tasks with filtering options"""
107 print("\n๐ View Tasks")
108 print("-" * 20)
109 print("Filters (press Enter to skip):")
110
111 status = input("Status (todo/in_progress/completed): ").strip().lower() or None
112 priority = input("Priority (low/medium/high/urgent): ").strip().lower() or None
113 tag = input("Tag: ").strip().lower() or None
114 overdue_only = input("Show overdue only? (y/n): ").strip().lower() == 'y'
115
116 result = self.task_controller.get_tasks(status, priority, tag, overdue_only)
117
118 if result['success']:
119 tasks = result['tasks']
120
121 if not tasks:
122 print("\n๐ญ No tasks found with the specified criteria.")
123 return
124
125 print(f"\n๐ Found {len(tasks)} task(s):")
126 print("=" * 80)
127
128 for i, task in enumerate(tasks, 1):
129 status_icon = {"completed": "โ
", "in_progress": "โณ", "todo": "๐"}.get(task['status'], "๐")
130 priority_icon = {"urgent": "๐ด", "high": "๐ก", "medium": "๐ข", "low": "โช"}.get(task['priority'], "๐ข")
131
132 print(f"{i:2}. {status_icon} {priority_icon} {task['title']}")
133 print(f" ๐ {task['description'][:60]}{'...' if len(task['description']) > 60 else ''}")
134
135 if task['due_date']:
136 due_date = task['due_date']
137 overdue_flag = " โ ๏ธ OVERDUE" if task.get('is_overdue') else ""
138 print(f" ๐
Due: {due_date}{overdue_flag}")
139
140 if task['tags']:
141 tags_str = " ".join(f"#{tag}" for tag in task['tags'])
142 print(f" ๐ท๏ธ {tags_str}")
143
144 print(f" ๐ ID: {task['id'][:8]}...")
145 print()
146 else:
147 print(f"โ Error retrieving tasks: {result['error']}")
148
149 def handle_create_task(self):
150 """Handle creating a new task"""
151 print("\nโ Create New Task")
152 print("-" * 20)
153
154 title = input("Task Title: ").strip()
155 if not title:
156 print("โ Title is required")
157 return
158
159 description = input("Description (optional): ").strip()
160
161 print("Priority options: low, medium, high, urgent")
162 priority = input("Priority (default: medium): ").strip().lower() or "medium"
163
164 due_date_str = input("Due Date (YYYY-MM-DD, optional): ").strip()
165 due_date = due_date_str if due_date_str else None
166
167 tags_str = input("Tags (comma-separated, optional): ").strip()
168 tags = [tag.strip() for tag in tags_str.split(",") if tag.strip()] if tags_str else None
169
170 result = self.task_controller.create_task(title, description, priority, due_date, tags)
171
172 if result['success']:
173 print(f"โ
{result['message']}")
174 print(f"๐ Task ID: {result['task']['id'][:8]}...")
175 else:
176 print(f"โ Error creating task:")
177 if isinstance(result['error'], list):
178 for error in result['error']:
179 print(f" โข {error}")
180 else:
181 print(f" โข {result['error']}")
182
183 def handle_dashboard(self):
184 """Display dashboard summary"""
185 result = self.task_controller.get_dashboard_summary()
186
187 if result['success']:
188 summary = result['summary']
189
190 print(generate_report_header())
191 print("๐ DASHBOARD SUMMARY")
192 print("=" * 60)
193
194 # Overview statistics
195 print(f"๐ Total Tasks: {summary['total_tasks']}")
196 print(f"โ
Completed: {summary['completed_tasks']}")
197 print(f"โณ In Progress: {summary['in_progress_tasks']}")
198 print(f"โ ๏ธ Overdue: {summary['overdue_tasks']}")
199 print(f"๐ Completion Rate: {summary['completion_rate']}%")
200
201 print("\n๐ฏ Priority Breakdown:")
202 for priority, count in summary['priority_breakdown'].items():
203 icon = {"urgent": "๐ด", "high": "๐ก", "medium": "๐ข", "low": "โช"}.get(priority, "๐")
204 print(f" {icon} {priority.capitalize()}: {count}")
205
206 # Recent activity
207 if summary['recent_activity']:
208 print("\n๐
Recent Activity:")
209 for activity in summary['recent_activity'][:5]:
210 timestamp = activity['timestamp'][:10] # Just the date
211 action_icon = "โ
" if activity['action'] == 'completed' else "โ๏ธ"
212 print(f" {action_icon} {activity['title']} ({timestamp})")
213
214 print("\n" + "=" * 60)
215 else:
216 print(f"โ Error generating dashboard: {result['error']}")
217
218 def run(self):
219 """Main application loop"""
220 self.display_banner()
221
222 if not self.authenticate_user():
223 print("\n๐ Goodbye!")
224 return
225
226 while True:
227 try:
228 self.display_main_menu()
229 choice = input("\nSelect an option (0-9): ").strip()
230
231 if choice == '0':
232 print("\n๐ Thank you for using Task Manager Pro!")
233 break
234 elif choice == '1':
235 self.handle_view_tasks()
236 elif choice == '2':
237 self.handle_create_task()
238 elif choice == '3':
239 self.handle_update_task()
240 elif choice == '4':
241 self.handle_delete_task()
242 elif choice == '5':
243 self.handle_dashboard()
244 elif choice == '6':
245 self.handle_search_tasks()
246 elif choice == '7':
247 self.handle_generate_report()
248 elif choice == '8':
249 self.handle_settings()
250 elif choice == '9':
251 self.current_user = None
252 if not self.authenticate_user():
253 break
254 else:
255 print("โ Invalid option. Please try again.")
256
257 input("\nPress Enter to continue...")
258
259 except KeyboardInterrupt:
260 print("\n\n๐ Goodbye!")
261 break
262 except Exception as e:
263 print(f"\nโ An error occurred: {str(e)}")
264 print("Please try again or contact support.")
265
266def main():
267 """Application entry point"""
268 try:
269 cli = TaskManagerCLI()
270 cli.run()
271 except Exception as e:
272 print(f"Critical error: {e}")
273 sys.exit(1)
274
275if __name__ == "__main__":
276 main()REST API Interface:
1# views/api.py
2from flask import Flask, request, jsonify
3from flask_cors import CORS
4from datetime import datetime
5from controllers.task_controller import TaskController
6from controllers.user_controller import UserController
7from models.database import JSONDatabase
8
9app = Flask(__name__)
10CORS(app)
11
12# Initialize controllers
13db = JSONDatabase()
14task_controller = TaskController(db)
15user_controller = UserController(db)
16
17@app.before_request
18def authenticate_request():
19 """Simple authentication middleware"""
20 if request.endpoint in ['auth.login', 'auth.register']:
21 return
22
23 # Simple token-based auth (in production, use proper JWT)
24 auth_header = request.headers.get('Authorization')
25 if not auth_header or not auth_header.startswith('Bearer '):
26 return jsonify({'error': 'Authentication required'}), 401
27
28 token = auth_header[7:] # Remove 'Bearer '
29 user = user_controller.validate_token(token)
30 if not user:
31 return jsonify({'error': 'Invalid token'}), 401
32
33 task_controller.set_current_user(user['id'])
34 request.current_user = user
35
36# Authentication endpoints
37@app.route('/api/auth/login', methods=['POST'])
38def login():
39 """User login endpoint"""
40 data = request.get_json()
41 username = data.get('username')
42 password = data.get('password')
43
44 result = user_controller.authenticate_user(username, password)
45
46 if result['success']:
47 return jsonify({
48 'success': True,
49 'user': result['user'],
50 'token': result['token']
51 })
52 else:
53 return jsonify({
54 'success': False,
55 'error': result['error']
56 }), 401
57
58@app.route('/api/auth/register', methods=['POST'])
59def register():
60 """User registration endpoint"""
61 data = request.get_json()
62
63 result = user_controller.register_user(
64 username=data.get('username'),
65 email=data.get('email'),
66 password=data.get('password'),
67 full_name=data.get('full_name')
68 )
69
70 if result['success']:
71 return jsonify(result), 201
72 else:
73 return jsonify(result), 400
74
75# Task endpoints
76@app.route('/api/tasks', methods=['GET'])
77def get_tasks():
78 """Get tasks with optional filtering"""
79 status = request.args.get('status')
80 priority = request.args.get('priority')
81 tag = request.args.get('tag')
82 overdue_only = request.args.get('overdue_only', '').lower() == 'true'
83
84 result = task_controller.get_tasks(status, priority, tag, overdue_only)
85 return jsonify(result)
86
87@app.route('/api/tasks', methods=['POST'])
88def create_task():
89 """Create a new task"""
90 data = request.get_json()
91
92 result = task_controller.create_task(
93 title=data.get('title'),
94 description=data.get('description', ''),
95 priority=data.get('priority', 'medium'),
96 due_date=data.get('due_date'),
97 tags=data.get('tags')
98 )
99
100 if result['success']:
101 return jsonify(result), 201
102 else:
103 return jsonify(result), 400
104
105@app.route('/api/tasks/<task_id>', methods=['PUT'])
106def update_task(task_id):
107 """Update a task"""
108 data = request.get_json()
109
110 result = task_controller.update_task(task_id, **data)
111
112 if result['success']:
113 return jsonify(result)
114 else:
115 return jsonify(result), 400
116
117@app.route('/api/tasks/<task_id>', methods=['DELETE'])
118def delete_task(task_id):
119 """Delete a task"""
120 result = task_controller.delete_task(task_id)
121
122 if result['success']:
123 return jsonify(result)
124 else:
125 return jsonify(result), 400
126
127@app.route('/api/dashboard', methods=['GET'])
128def get_dashboard():
129 """Get dashboard summary"""
130 result = task_controller.get_dashboard_summary()
131 return jsonify(result)
132
133# Health check endpoint
134@app.route('/api/health', methods=['GET'])
135def health_check():
136 """API health check"""
137 return jsonify({
138 'status': 'healthy',
139 'timestamp': datetime.now().isoformat(),
140 'version': '1.0.0'
141 })
142
143if __name__ == '__main__':
144 app.run(debug=True, host='0.0.0.0', port=5000)Integration Benefits:
Professional Python applications require comprehensive testing, documentation, and deployment strategies for production readiness.
Test Suite Implementation:
1# tests/test_models.py
2import unittest
3from datetime import date, datetime
4from models.task import Task, Priority, Status
5from models.user import User
6
7class TestTaskModel(unittest.TestCase):
8 """Test cases for Task model"""
9
10 def setUp(self):
11 """Set up test fixtures"""
12 self.task = Task(
13 title="Test Task",
14 description="Test description",
15 priority=Priority.HIGH,
16 due_date=date(2024, 12, 31)
17 )
18
19 def test_task_creation(self):
20 """Test basic task creation"""
21 self.assertEqual(self.task.title, "Test Task")
22 self.assertEqual(self.task.priority, Priority.HIGH)
23 self.assertEqual(self.task.status, Status.TODO)
24 self.assertIsNotNone(self.task.id)
25 self.assertIsInstance(self.task.created_at, datetime)
26
27 def test_task_completion(self):
28 """Test marking task as completed"""
29 self.assertIsNone(self.task.completed_at)
30 self.task.mark_completed()
31 self.assertEqual(self.task.status, Status.COMPLETED)
32 self.assertIsNotNone(self.task.completed_at)
33
34 def test_overdue_detection(self):
35 """Test overdue task detection"""
36 # Task with future due date should not be overdue
37 future_task = Task("Future Task", due_date=date(2025, 12, 31))
38 self.assertFalse(future_task.is_overdue())
39
40 # Task with past due date should be overdue
41 past_task = Task("Past Task", due_date=date(2020, 1, 1))
42 self.assertTrue(past_task.is_overdue())
43
44 # Completed tasks should not be overdue
45 past_task.mark_completed()
46 self.assertFalse(past_task.is_overdue())
47
48 def test_subtask_progress(self):
49 """Test subtask progress calculation"""
50 # Task with no subtasks
51 self.assertEqual(self.task.get_progress_percentage(), 0.0)
52
53 # Add subtasks
54 subtask1 = Task("Subtask 1")
55 subtask2 = Task("Subtask 2")
56 self.task.add_subtask(subtask1)
57 self.task.add_subtask(subtask2)
58
59 # No completed subtasks
60 self.assertEqual(self.task.get_progress_percentage(), 0.0)
61
62 # Complete one subtask
63 subtask1.mark_completed()
64 self.assertEqual(self.task.get_progress_percentage(), 50.0)
65
66 # Complete all subtasks
67 subtask2.mark_completed()
68 self.assertEqual(self.task.get_progress_percentage(), 100.0)
69
70 def test_task_serialization(self):
71 """Test task to/from dictionary conversion"""
72 task_dict = self.task.to_dict()
73
74 # Check required fields
75 self.assertIn('id', task_dict)
76 self.assertIn('title', task_dict)
77 self.assertIn('priority', task_dict)
78 self.assertIn('status', task_dict)
79
80 # Create task from dictionary
81 restored_task = Task.from_dict(task_dict)
82 self.assertEqual(restored_task.title, self.task.title)
83 self.assertEqual(restored_task.priority, self.task.priority)
84 self.assertEqual(restored_task.id, self.task.id)
85
86class TestUserModel(unittest.TestCase):
87 """Test cases for User model"""
88
89 def setUp(self):
90 """Set up test fixtures"""
91 self.user = User(
92 username="testuser",
93 email="[email protected]",
94 password="SecurePass123",
95 full_name="Test User"
96 )
97
98 def test_user_creation(self):
99 """Test basic user creation"""
100 self.assertEqual(self.user.username, "testuser")
101 self.assertEqual(self.user.email, "[email protected]")
102 self.assertEqual(self.user.full_name, "Test User")
103 self.assertIsNotNone(self.user.password_hash)
104 self.assertNotEqual(self.user.password_hash, "SecurePass123") # Should be hashed
105
106 def test_password_verification(self):
107 """Test password verification"""
108 self.assertTrue(self.user.verify_password("SecurePass123"))
109 self.assertFalse(self.user.verify_password("wrongpassword"))
110
111 def test_user_serialization(self):
112 """Test user serialization (excluding sensitive data)"""
113 user_dict = self.user.to_dict()
114
115 # Should include safe fields
116 self.assertIn('username', user_dict)
117 self.assertIn('email', user_dict)
118 self.assertIn('full_name', user_dict)
119
120 # Should exclude sensitive fields
121 self.assertNotIn('password_hash', user_dict)
122
123# tests/test_controllers.py
124import unittest
125from unittest.mock import Mock, patch
126from controllers.task_controller import TaskController
127from models.database import JSONDatabase
128
129class TestTaskController(unittest.TestCase):
130 """Test cases for TaskController"""
131
132 def setUp(self):
133 """Set up test fixtures"""
134 self.mock_db = Mock(spec=JSONDatabase)
135 self.controller = TaskController(self.mock_db)
136 self.controller.set_current_user("test_user_id")
137
138 def test_create_task_success(self):
139 """Test successful task creation"""
140 self.mock_db.add_task.return_value = True
141
142 result = self.controller.create_task(
143 title="Test Task",
144 description="Test description",
145 priority="high"
146 )
147
148 self.assertTrue(result['success'])
149 self.assertIn('message', result)
150 self.assertIn('task', result)
151 self.mock_db.add_task.assert_called_once()
152
153 def test_create_task_validation_error(self):
154 """Test task creation with validation errors"""
155 result = self.controller.create_task(
156 title="", # Empty title should cause validation error
157 description="Test description"
158 )
159
160 self.assertFalse(result['success'])
161 self.assertIn('error', result)
162 self.mock_db.add_task.assert_not_called()
163
164 def test_get_tasks_filtering(self):
165 """Test task retrieval with filtering"""
166 # Mock database response
167 mock_tasks = [
168 {
169 'id': '1', 'title': 'Task 1', 'status': 'todo',
170 'priority': 'high', 'tags': ['work'], 'created_at': '2024-01-01T00:00:00',
171 'updated_at': '2024-01-01T00:00:00', 'due_date': None,
172 'assignee': 'test_user_id'
173 },
174 {
175 'id': '2', 'title': 'Task 2', 'status': 'completed',
176 'priority': 'medium', 'tags': ['personal'], 'created_at': '2024-01-01T00:00:00',
177 'updated_at': '2024-01-01T00:00:00', 'due_date': None,
178 'assignee': 'test_user_id'
179 }
180 ]
181 self.mock_db.get_tasks.return_value = mock_tasks
182
183 # Test filtering by status
184 result = self.controller.get_tasks(status='todo')
185 self.assertTrue(result['success'])
186 self.assertEqual(len(result['tasks']), 1)
187 self.assertEqual(result['tasks'][0]['status'], 'todo')
188
189# tests/test_integration.py
190import unittest
191import tempfile
192import os
193from models.database import JSONDatabase
194from controllers.task_controller import TaskController
195from controllers.user_controller import UserController
196
197class TestIntegration(unittest.TestCase):
198 """Integration tests for the complete system"""
199
200 def setUp(self):
201 """Set up test environment with temporary database"""
202 self.temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.json')
203 self.temp_file.close()
204
205 self.db = JSONDatabase(self.temp_file.name)
206 self.task_controller = TaskController(self.db)
207 self.user_controller = UserController(self.db)
208
209 def tearDown(self):
210 """Clean up test environment"""
211 os.unlink(self.temp_file.name)
212
213 def test_complete_workflow(self):
214 """Test complete user workflow: register, login, create tasks, manage tasks"""
215
216 # 1. Register user
217 register_result = self.user_controller.register_user(
218 username="testuser",
219 email="[email protected]",
220 password="SecurePass123",
221 full_name="Test User"
222 )
223 self.assertTrue(register_result['success'])
224
225 # 2. Login user
226 login_result = self.user_controller.authenticate_user("testuser", "SecurePass123")
227 self.assertTrue(login_result['success'])
228
229 user_id = login_result['user']['id']
230 self.task_controller.set_current_user(user_id)
231
232 # 3. Create tasks
233 task1_result = self.task_controller.create_task(
234 title="Complete Project",
235 description="Finish the Python project",
236 priority="high"
237 )
238 self.assertTrue(task1_result['success'])
239
240 task2_result = self.task_controller.create_task(
241 title="Review Code",
242 description="Code review for the team",
243 priority="medium"
244 )
245 self.assertTrue(task2_result['success'])
246
247 # 4. Get tasks
248 tasks_result = self.task_controller.get_tasks()
249 self.assertTrue(tasks_result['success'])
250 self.assertEqual(len(tasks_result['tasks']), 2)
251
252 # 5. Update task
253 task_id = task1_result['task']['id']
254 update_result = self.task_controller.update_task(
255 task_id,
256 status='completed'
257 )
258 self.assertTrue(update_result['success'])
259
260 # 6. Generate dashboard
261 dashboard_result = self.task_controller.get_dashboard_summary()
262 self.assertTrue(dashboard_result['success'])
263 self.assertEqual(dashboard_result['summary']['total_tasks'], 2)
264 self.assertEqual(dashboard_result['summary']['completed_tasks'], 1)
265
266# Run tests
267if __name__ == '__main__':
268 # Create test suite
269 test_suite = unittest.TestSuite()
270
271 # Add test cases
272 test_suite.addTest(unittest.makeSuite(TestTaskModel))
273 test_suite.addTest(unittest.makeSuite(TestUserModel))
274 test_suite.addTest(unittest.makeSuite(TestTaskController))
275 test_suite.addTest(unittest.makeSuite(TestIntegration))
276
277 # Run tests
278 runner = unittest.TextTestRunner(verbosity=2)
279 result = runner.run(test_suite)
280
281 # Print summary
282 print(f"\n{'='*60}")
283 print(f"TESTS COMPLETED: {result.testsRun} tests")
284 print(f"FAILURES: {len(result.failures)}")
285 print(f"ERRORS: {len(result.errors)}")
286
287 if result.failures:
288 print("\nFAILURES:")
289 for test, traceback in result.failures:
290 print(f" - {test}: {traceback}")
291
292 if result.errors:
293 print("\nERRORS:")
294 for test, traceback in result.errors:
295 print(f" - {test}: {traceback}")Documentation and Deployment:
1# setup.py - Package configuration
2from setuptools import setup, find_packages
3
4with open("README.md", "r", encoding="utf-8") as fh:
5 long_description = fh.read()
6
7with open("requirements.txt", "r", encoding="utf-8") as fh:
8 requirements = [line.strip() for line in fh if line.strip() and not line.startswith("#")]
9
10setup(
11 name="task-manager-pro",
12 version="1.0.0",
13 author="Python Developer",
14 author_email="[email protected]",
15 description="Professional task management system built with Python",
16 long_description=long_description,
17 long_description_content_type="text/markdown",
18 url="https://github.com/username/task-manager-pro",
19 packages=find_packages(),
20 classifiers=[
21 "Development Status :: 5 - Production/Stable",
22 "Intended Audience :: End Users/Desktop",
23 "License :: OSI Approved :: MIT License",
24 "Operating System :: OS Independent",
25 "Programming Language :: Python :: 3",
26 "Programming Language :: Python :: 3.8",
27 "Programming Language :: Python :: 3.9",
28 "Programming Language :: Python :: 3.10",
29 "Topic :: Office/Business",
30 "Topic :: Software Development :: Libraries :: Python Modules",
31 ],
32 python_requires=">=3.8",
33 install_requires=requirements,
34 extras_require={
35 "dev": [
36 "pytest>=6.0",
37 "black>=21.0",
38 "flake8>=3.8",
39 "mypy>=0.910",
40 "coverage>=5.0"
41 ],
42 "api": [
43 "flask>=2.0",
44 "flask-cors>=3.0",
45 ]
46 },
47 entry_points={
48 "console_scripts": [
49 "task-manager=main:main",
50 ],
51 },
52 include_package_data=True,
53 zip_safe=False,
54)
55
56# Dockerfile - Container deployment
57FROM python:3.10-slim
58
59WORKDIR /app
60
61COPY requirements.txt .
62RUN pip install --no-cache-dir -r requirements.txt
63
64COPY . .
65
66# Create non-root user for security
67RUN useradd --create-home --shell /bin/bash taskuser && \
68 chown -R taskuser:taskuser /app
69USER taskuser
70
71# Expose port for API
72EXPOSE 5000
73
74# Health check
75HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
76 CMD python -c "import requests; requests.get('http://localhost:5000/api/health')" || exit 1
77
78# Default command
79CMD ["python", "-m", "views.api"]
80
81# docker-compose.yml - Multi-service deployment
82version: '3.8'
83
84services:
85 task-manager:
86 build: .
87 ports:
88 - "5000:5000"
89 environment:
90 - FLASK_ENV=production
91 - DATABASE_PATH=/app/data/tasks.json
92 volumes:
93 - task_data:/app/data
94 restart: unless-stopped
95 healthcheck:
96 test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"]
97 interval: 30s
98 timeout: 10s
99 retries: 3
100
101 nginx:
102 image: nginx:alpine
103 ports:
104 - "80:80"
105 volumes:
106 - ./nginx.conf:/etc/nginx/nginx.conf
107 depends_on:
108 - task-manager
109 restart: unless-stopped
110
111volumes:
112 task_data:
113
114# Makefile - Development workflow automation
115.PHONY: help install test lint format clean run-cli run-api docker-build docker-run
116
117help:
118 @echo "Available commands:"
119 @echo " install Install dependencies"
120 @echo " test Run test suite"
121 @echo " lint Run code linting"
122 @echo " format Format code with black"
123 @echo " clean Clean up temporary files"
124 @echo " run-cli Run CLI application"
125 @echo " run-api Run API server"
126 @echo " docker-build Build Docker image"
127 @echo " docker-run Run Docker container"
128
129install:
130 pip install -r requirements.txt
131 pip install -e .
132
133test:
134 python -m pytest tests/ -v --cov=. --cov-report=html
135
136lint:
137 flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
138 mypy . --ignore-missing-imports
139
140format:
141 black . --line-length 88
142 isort . --profile black
143
144clean:
145 find . -type f -name "*.pyc" -delete
146 find . -type d -name "__pycache__" -delete
147 rm -rf .pytest_cache
148 rm -rf htmlcov
149 rm -rf *.egg-info
150
151run-cli:
152 python main.py
153
154run-api:
155 python -m views.api
156
157docker-build:
158 docker build -t task-manager-pro .
159
160docker-run:
161 docker-compose up -dProfessional Development Practices:
Run the complete application with all integrated features
1# Complete Task Management System - Integration Demo
2# This demonstrates the full application with all components working together
3
4import sys
5import json
6from datetime import datetime, date, timedelta
7from pathlib import Path
8
9# Import all our components
10from models.task import Task, Priority, Status
11from models.user import User
12from models.database import JSONDatabase
13from controllers.task_controller import TaskController
14from controllers.user_controller import UserController
15from utils.helpers import generate_report_header, format_output
16
17class DemoTaskManager:
18 """Complete demonstration of the Task Management System"""
19
20 def __init__(self):
21 # Initialize the system
22 self.db = JSONDatabase("demo_tasks.json")
23 self.task_controller = TaskController(self.db)
24 self.user_controller = UserController(self.db)
25 self.demo_user = None
26
27 def setup_demo_data(self):
28 """Set up demonstration data"""
29 print("๐ง Setting up demonstration data...")
30
31 # Register demo user
32 user_result = self.user_controller.register_user(
33 username="demo_user",
34 email="[email protected]",
35 password="DemoPass123",
36 full_name="Demo User"
37 )
38
39 if user_result['success']:
40 # Login the demo user
41 login_result = self.user_controller.authenticate_user("demo_user", "DemoPass123")
42 if login_result['success']:
43 self.demo_user = login_result['user']
44 self.task_controller.set_current_user(self.demo_user['id'])
45 print(f"โ
Demo user '{self.demo_user['full_name']}' created and logged in")
46 else:
47 print("โ Failed to login demo user")
48 return False
49 else:
50 # User might already exist, try to login
51 login_result = self.user_controller.authenticate_user("demo_user", "DemoPass123")
52 if login_result['success']:
53 self.demo_user = login_result['user']
54 self.task_controller.set_current_user(self.demo_user['id'])
55 print(f"โ
Existing demo user '{self.demo_user['full_name']}' logged in")
56 else:
57 print("โ Failed to setup demo user")
58 return False
59
60 # Create sample tasks
61 sample_tasks = [
62 {
63 'title': 'Complete Python Course',
64 'description': 'Finish all lessons in the Python Fundamentals course',
65 'priority': 'high',
66 'due_date': (date.today() + timedelta(days=7)).isoformat(),
67 'tags': ['learning', 'programming', 'python']
68 },
69 {
70 'title': 'Build Portfolio Project',
71 'description': 'Create a comprehensive task management application',
72 'priority': 'urgent',
73 'due_date': (date.today() + timedelta(days=14)).isoformat(),
74 'tags': ['portfolio', 'development', 'career']
75 },
76 {
77 'title': 'Code Review Meeting',
78 'description': 'Review team code and provide feedback',
79 'priority': 'medium',
80 'due_date': (date.today() + timedelta(days=1)).isoformat(),
81 'tags': ['teamwork', 'review']
82 },
83 {
84 'title': 'Update Documentation',
85 'description': 'Update project documentation with recent changes',
86 'priority': 'low',
87 'due_date': (date.today() + timedelta(days=30)).isoformat(),
88 'tags': ['documentation', 'maintenance']
89 },
90 {
91 'title': 'Fix Critical Bug',
92 'description': 'Resolve the authentication issue reported by users',
93 'priority': 'urgent',
94 'due_date': (date.today() + timedelta(days=2)).isoformat(),
95 'tags': ['bug', 'urgent', 'security']
96 },
97 {
98 'title': 'Learn Docker',
99 'description': 'Study Docker containerization for deployment',
100 'priority': 'medium',
101 'due_date': None,
102 'tags': ['learning', 'devops', 'docker']
103 }
104 ]
105
106 created_count = 0
107 for task_data in sample_tasks:
108 result = self.task_controller.create_task(**task_data)
109 if result['success']:
110 created_count += 1
111
112 print(f"โ
Created {created_count} sample tasks")
113
114 # Complete some tasks to show progress
115 tasks_result = self.task_controller.get_tasks()
116 if tasks_result['success'] and tasks_result['tasks']:
117 # Complete the first task
118 first_task_id = tasks_result['tasks'][0]['id']
119 self.task_controller.update_task(first_task_id, status='completed')
120
121 # Set one task as in progress
122 if len(tasks_result['tasks']) > 1:
123 second_task_id = tasks_result['tasks'][1]['id']
124 self.task_controller.update_task(second_task_id, status='in_progress')
125
126 print("โ
Demo data setup complete!\n")
127 return True
128
129 def demonstrate_core_features(self):
130 """Demonstrate all core features of the system"""
131
132 print(generate_report_header())
133 print("๐ TASK MANAGEMENT SYSTEM DEMONSTRATION")
134 print("=" * 80)
135
136 # 1. Dashboard Overview
137 print("\n๐ DASHBOARD OVERVIEW")
138 print("-" * 40)
139 dashboard_result = self.task_controller.get_dashboard_summary()
140
141 if dashboard_result['success']:
142 summary = dashboard_result['summary']
143
144 print(f"๐ Total Tasks: {summary['total_tasks']}")
145 print(f"โ
Completed: {summary['completed_tasks']}")
146 print(f"โณ In Progress: {summary['in_progress_tasks']}")
147 print(f"โ ๏ธ Overdue: {summary['overdue_tasks']}")
148 print(f"๐ Completion Rate: {summary['completion_rate']}%")
149
150 print("\n๐ฏ Priority Breakdown:")
151 for priority, count in summary['priority_breakdown'].items():
152 icon = {"urgent": "๐ด", "high": "๐ก", "medium": "๐ข", "low": "โช"}[priority]
153 print(f" {icon} {priority.capitalize()}: {count} tasks")
154
155 # 2. Task Listing with Different Views
156 print("\n\n๐ TASK MANAGEMENT FEATURES")
157 print("-" * 40)
158
159 # All tasks
160 print("\n๐ All Tasks:")
161 all_tasks_result = self.task_controller.get_tasks()
162 if all_tasks_result['success']:
163 self.display_tasks(all_tasks_result['tasks'][:5]) # Show first 5
164
165 # High priority tasks
166 print("\n๐ฅ High Priority Tasks:")
167 high_priority_result = self.task_controller.get_tasks(priority='high')
168 if high_priority_result['success']:
169 self.display_tasks(high_priority_result['tasks'])
170
171 # Urgent tasks
172 print("\n๐จ Urgent Tasks:")
173 urgent_tasks_result = self.task_controller.get_tasks(priority='urgent')
174 if urgent_tasks_result['success']:
175 self.display_tasks(urgent_tasks_result['tasks'])
176
177 # Tasks with specific tag
178 print("\n๐ท๏ธ Learning-Related Tasks:")
179 learning_tasks_result = self.task_controller.get_tasks(tag='learning')
180 if learning_tasks_result['success']:
181 self.display_tasks(learning_tasks_result['tasks'])
182
183 # 3. Task Operations Demonstration
184 print("\n\n๐ ๏ธ TASK OPERATIONS DEMONSTRATION")
185 print("-" * 40)
186
187 # Create a new task
188 print("\nโ Creating a new task...")
189 new_task_result = self.task_controller.create_task(
190 title="Demo: API Integration",
191 description="Integrate with external weather API for the project",
192 priority="medium",
193 due_date=(date.today() + timedelta(days=10)).isoformat(),
194 tags=['api', 'integration', 'demo']
195 )
196
197 if new_task_result['success']:
198 print(f"โ
Task created: {new_task_result['task']['title']}")
199 new_task_id = new_task_result['task']['id']
200
201 # Update the task
202 print("\nโ๏ธ Updating the task...")
203 update_result = self.task_controller.update_task(
204 new_task_id,
205 description="Integrate with OpenWeatherMap API and display weather data",
206 priority="high",
207 add_tag="weather"
208 )
209
210 if update_result['success']:
211 print(f"โ
Task updated: {update_result['task']['title']}")
212
213 # Mark as in progress
214 print("\nโณ Marking task as in progress...")
215 progress_result = self.task_controller.update_task(new_task_id, status='in_progress')
216 if progress_result['success']:
217 print("โ
Task marked as in progress")
218
219 # 4. Advanced Features
220 print("\n\n๐ฌ ADVANCED FEATURES")
221 print("-" * 40)
222
223 # Database statistics
224 stats = self.db.get_statistics()
225 print(f"\n๐ Database Statistics:")
226 print(f" Total Tasks: {stats['total_tasks']}")
227 print(f" Completed Tasks: {stats['completed_tasks']}")
228 print(f" Completion Rate: {stats['completion_rate']:.1f}%")
229 print(f" Total Users: {stats['total_users']}")
230 print(f" Database Size: {stats['database_size']} bytes")
231
232 # Recent activity
233 if dashboard_result['success'] and dashboard_result['summary']['recent_activity']:
234 print(f"\n๐
Recent Activity:")
235 for activity in dashboard_result['summary']['recent_activity'][:3]:
236 action_icon = "โ
" if activity['action'] == 'completed' else "โ๏ธ"
237 print(f" {action_icon} {activity['title']}")
238
239 # 5. Data Export Demonstration
240 print("\n\n๐พ DATA EXPORT CAPABILITIES")
241 print("-" * 40)
242
243 # Export tasks to JSON
244 export_tasks = self.task_controller.get_tasks()
245 if export_tasks['success']:
246 export_data = {
247 'export_timestamp': datetime.now().isoformat(),
248 'user': self.demo_user['username'],
249 'total_tasks': len(export_tasks['tasks']),
250 'tasks': export_tasks['tasks']
251 }
252
253 export_file = 'task_export_demo.json'
254 try:
255 with open(export_file, 'w') as f:
256 json.dump(export_data, f, indent=2)
257 print(f"โ
Tasks exported to {export_file}")
258 print(f" ๐ {len(export_tasks['tasks'])} tasks exported")
259 print(f" ๐ Export includes metadata and complete task details")
260 except Exception as e:
261 print(f"โ Export failed: {e}")
262
263 # 6. System Health Check
264 print("\n\n๐ฅ SYSTEM HEALTH CHECK")
265 print("-" * 40)
266
267 health_status = {
268 'database_accessible': True,
269 'user_authenticated': self.demo_user is not None,
270 'tasks_retrievable': all_tasks_result['success'],
271 'task_creation_working': new_task_result['success'] if 'new_task_result' in locals() else False,
272 'data_export_working': Path(export_file).exists() if 'export_file' in locals() else False
273 }
274
275 print("System Health Status:")
276 for check, status in health_status.items():
277 icon = "โ
" if status else "โ"
278 print(f" {icon} {check.replace('_', ' ').title()}: {'PASS' if status else 'FAIL'}")
279
280 overall_health = all(health_status.values())
281 health_icon = "๐ข" if overall_health else "๐ด"
282 print(f"\n{health_icon} Overall System Status: {'HEALTHY' if overall_health else 'ISSUES DETECTED'}")
283
284 print("\n" + "=" * 80)
285 print("๐ DEMONSTRATION COMPLETE!")
286 print("\nKey Features Demonstrated:")
287 print("โข โ
User authentication and session management")
288 print("โข โ
Task CRUD operations (Create, Read, Update, Delete)")
289 print("โข โ
Advanced filtering and search capabilities")
290 print("โข โ
Priority-based task management")
291 print("โข โ
Tag-based organization system")
292 print("โข โ
Dashboard with comprehensive analytics")
293 print("โข โ
Data export and backup functionality")
294 print("โข โ
Professional error handling and validation")
295 print("โข โ
Scalable architecture with separation of concerns")
296 print("โข โ
Production-ready code with proper documentation")
297
298 print("\n๐ก This system demonstrates enterprise-level Python development:")
299 print(" ๐๏ธ Object-Oriented Programming with professional design patterns")
300 print(" ๐๏ธ Database operations with JSON-based storage")
301 print(" ๐ User authentication and authorization")
302 print(" ๐งช Comprehensive error handling and validation")
303 print(" ๐ Business intelligence with dashboard analytics")
304 print(" ๐ Scalable architecture ready for production deployment")
305
306 def display_tasks(self, tasks):
307 """Display tasks in a formatted manner"""
308 if not tasks:
309 print(" ๐ญ No tasks found")
310 return
311
312 for task in tasks:
313 status_icon = {"completed": "โ
", "in_progress": "โณ", "todo": "๐"}[task['status']]
314 priority_icon = {"urgent": "๐ด", "high": "๐ก", "medium": "๐ข", "low": "โช"}[task['priority']]
315
316 print(f" {status_icon} {priority_icon} {task['title']}")
317
318 if task['due_date']:
319 due_date = task['due_date']
320 overdue_flag = " โ ๏ธ OVERDUE" if task.get('is_overdue') else ""
321 print(f" ๐
Due: {due_date}{overdue_flag}")
322
323 if task['tags']:
324 tags_str = " ".join(f"#{tag}" for tag in task['tags'])
325 print(f" ๐ท๏ธ {tags_str}")
326
327 def run_complete_demo(self):
328 """Run the complete demonstration"""
329 try:
330 print("๐ Starting Complete Task Management System Demo\n")
331
332 # Setup demo data
333 if not self.setup_demo_data():
334 print("โ Failed to setup demo data")
335 return False
336
337 # Demonstrate all features
338 self.demonstrate_core_features()
339
340 return True
341
342 except Exception as e:
343 print(f"โ Demo failed with error: {e}")
344 import traceback
345 traceback.print_exc()
346 return False
347
348 finally:
349 print("\n๐งน Cleaning up demo files...")
350 # Cleanup demo files
351 demo_files = ['demo_tasks.json', 'task_export_demo.json']
352 for file in demo_files:
353 if Path(file).exists():
354 try:
355 Path(file).unlink()
356 print(f" ๐๏ธ Removed {file}")
357 except Exception as e:
358 print(f" โ ๏ธ Could not remove {file}: {e}")
359
360def main():
361 """Main demo function"""
362 print("=" * 80)
363 print("๐ PYTHON FUNDAMENTALS - COMPLETE PROJECT DEMONSTRATION")
364 print("๐ฏ Task Management System - Enterprise Grade Implementation")
365 print("=" * 80)
366
367 demo = DemoTaskManager()
368 success = demo.run_complete_demo()
369
370 if success:
371 print("\n๐ Demo completed successfully!")
372 print("\nThis project demonstrates mastery of:")
373 print("โข Object-Oriented Programming")
374 print("โข Database Design and Operations")
375 print("โข Error Handling and Validation")
376 print("โข User Authentication and Security")
377 print("โข Professional Code Architecture")
378 print("โข Testing and Documentation")
379 print("โข Production Deployment Practices")
380
381 print("\n๐ Ready for:")
382 print("โข Portfolio presentation")
383 print("โข Technical interviews")
384 print("โข Production deployment")
385 print("โข Team collaboration")
386 print("โข Enterprise development")
387 else:
388 print("\nโ Demo encountered issues - check error messages above")
389 sys.exit(1)
390
391if __name__ == "__main__":
392 main()Your progress is saved in this browser, on this device only.