پروژه نهایی – مدیر وظایف خط فرمان
در این فصل پایانی، با کنار هم گذاشتن همه چیزهایی که در این دوره یاد گرفتیم، یک پروژه واقعی میسازیم: مدیر وظایف خط فرمان (Task Manager CLI). این پروژه از کلاسها، فایلها، JSON، Exception Handling و argparse استفاده میکند.
در این فصل پایانی، با کنار هم گذاشتن همه چیزهایی که در این دوره یاد گرفتیم، یک پروژه واقعی میسازیم: مدیر وظایف خط فرمان (Task Manager CLI). این پروژه از کلاسها، فایلها، JSON، Exception Handling و argparse استفاده میکند.
معرفی پروژه
برنامهای میسازیم که:
- وظایف (Tasks) را در فایل JSON ذخیره میکند
- امکان افزودن، حذف، تکمیل و لیست کردن وظایف را دارد
- دو حالت اجرا دارد: منوی تعاملی و خط فرمان (argparse)
- از تاریخ شمسی استفاده میکند
- خطاها را بهخوبی مدیریت میکند
ساختار پروژه
task_manager/
├── task_manager.py # فایل اصلی
├── tasks.json # محل ذخیره وظایف (خودکار ساخته میشود)
└── README.md # توضیحات
کلاس Task
هر وظیفه یک شیء از کلاس Task است:
from datetime import datetime
class Task:
def __init__(self, task_id, title, done=False, created_at=None):
self.task_id = task_id
self.title = title
self.done = done
self.created_at = created_at or datetime.now().isoformat()
def to_dict(self):
"""تبدیل به دیکشنری برای ذخیره در JSON"""
return {
"id": self.task_id,
"title": self.title,
"done": self.done,
"created_at": self.created_at
}
@classmethod
def from_dict(cls, data):
"""ساخت Task از دیکشنری (از JSON)"""
return cls(
task_id=data["id"],
title=data["title"],
done=data["done"],
created_at=data["created_at"]
)
def __str__(self):
status = "✓" if self.done else "○"
return f"[{status}] #{self.task_id} - {self.title}"
کلاس TaskManager
این کلاس مسئول مدیریت لیست وظایف و ذخیره/بازیابی از فایل است:
import json
from pathlib import Path
class TaskManager:
def __init__(self, filename="tasks.json"):
self.filename = Path(filename)
self.tasks = []
self.load()
def load(self):
"""خواندن وظایف از فایل JSON"""
if not self.filename.exists():
self.tasks = []
return
try:
with open(self.filename, "r", encoding="utf-8") as f:
data = json.load(f)
self.tasks = [Task.from_dict(t) for t in data]
except (json.JSONDecodeError, KeyError) as e:
print(f"خطا در خواندن فایل: {e}")
self.tasks = []
def save(self):
"""ذخیره وظایف در فایل JSON"""
data = [t.to_dict() for t in self.tasks]
with open(self.filename, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def get_next_id(self):
"""تولید ID جدید"""
if not self.tasks:
return 1
return max(t.task_id for t in self.tasks) + 1
def add(self, title):
"""افزودن وظیفه جدید"""
if not title.strip():
raise ValueError("عنوان نمیتواند خالی باشد")
task = Task(self.get_next_id(), title.strip())
self.tasks.append(task)
self.save()
return task
def remove(self, task_id):
"""حذف وظیفه با ID"""
task = self.find(task_id)
if not task:
raise ValueError(f"وظیفهای با شناسه {task_id} یافت نشد")
self.tasks.remove(task)
self.save()
return task
def complete(self, task_id):
"""تکمیل وظیفه"""
task = self.find(task_id)
if not task:
raise ValueError(f"وظیفهای با شناسه {task_id} یافت نشد")
task.done = True
self.save()
return task
def find(self, task_id):
"""جستجوی وظیفه با ID"""
for task in self.tasks:
if task.task_id == task_id:
return task
return None
def list_all(self, show_done=True):
"""لیست همه وظایف"""
if show_done:
return self.tasks
return [t for t in self.tasks if not t.done]
def stats(self):
"""آمار وظایف"""
total = len(self.tasks)
done = sum(1 for t in self.tasks if t.done)
pending = total - done
return {"total": total, "done": done, "pending": pending}
منوی تعاملی
تابع اصلی برنامه با منوی تعاملی:
def print_menu():
print("n" + "=" * 40)
print(" مدیر وظایف - ICSD")
print("=" * 40)
print("1. افزودن وظیفه")
print("2. نمایش همه وظایف")
print("3. نمایش وظایف باز")
print("4. تکمیل وظیفه")
print("5. حذف وظیفه")
print("6. آمار")
print("0. خروج")
print("=" * 40)
def interactive_mode():
manager = TaskManager()
while True:
print_menu()
try:
choice = input("انتخاب شما: ").strip()
if choice == "0":
print("خداحافظ! 👋")
break
elif choice == "1":
title = input("عنوان وظیفه: ")
task = manager.add(title)
print(f"✓ افزوده شد: {task}")
elif choice == "2":
tasks = manager.list_all()
if not tasks:
print("هیچ وظیفهای وجود ندارد.")
else:
for t in tasks:
print(f" {t}")
elif choice == "3":
tasks = manager.list_all(show_done=False)
if not tasks:
print("هیچ وظیفه باز وجود ندارد.")
else:
for t in tasks:
print(f" {t}")
elif choice == "4":
task_id = int(input("شناسه وظیفه: "))
task = manager.complete(task_id)
print(f"✓ تکمیل شد: {task}")
elif choice == "5":
task_id = int(input("شناسه وظیفه برای حذف: "))
confirm = input("مطمئن هستید؟ (y/n): ").lower()
if confirm == "y":
task = manager.remove(task_id)
print(f"✓ حذف شد: {task.title}")
elif choice == "6":
s = manager.stats()
print(f"کل: {s['total']} | تکمیلشده: {s['done']} | باز: {s['pending']}")
else:
print("⚠ گزینه نامعتبر")
except ValueError as e:
print(f"⚠ خطا: {e}")
except KeyboardInterrupt:
print("nخروج...")
break
حالت خط فرمان با argparse
برای استفاده غیرتعاملی (مثلاً در اسکریپتهای دیگر):
import argparse
def cli_mode():
parser = argparse.ArgumentParser(
description="مدیر وظایف خط فرمان",
prog="task_manager"
)
subparsers = parser.add_subparsers(dest="command", help="دستورات")
# add
add_parser = subparsers.add_parser("add", help="افزودن وظیفه")
add_parser.add_argument("title", help="عنوان وظیفه")
# list
list_parser = subparsers.add_parser("list", help="نمایش وظایف")
list_parser.add_argument("--pending", action="store_true", help="فقط باز")
# done
done_parser = subparsers.add_parser("done", help="تکمیل وظیفه")
done_parser.add_argument("id", type=int, help="شناسه")
# remove
rm_parser = subparsers.add_parser("remove", help="حذف وظیفه")
rm_parser.add_argument("id", type=int)
# stats
subparsers.add_parser("stats", help="آمار")
args = parser.parse_args()
manager = TaskManager()
try:
if args.command == "add":
task = manager.add(args.title)
print(f"✓ افزوده شد: {task}")
elif args.command == "list":
tasks = manager.list_all(show_done=not args.pending)
for t in tasks:
print(t)
elif args.command == "done":
task = manager.complete(args.id)
print(f"✓ تکمیل شد: {task}")
elif args.command == "remove":
task = manager.remove(args.id)
print(f"✓ حذف شد: {task.title}")
elif args.command == "stats":
s = manager.stats()
print(f"کل: {s['total']} | تکمیل: {s['done']} | باز: {s['pending']}")
else:
parser.print_help()
except ValueError as e:
print(f"خطا: {e}")
return 1
return 0
تابع main
تشخیص حالت اجرا (تعاملی یا CLI):
import sys
def main():
if len(sys.argv) > 1:
# حالت CLI
sys.exit(cli_mode())
else:
# حالت تعاملی
interactive_mode()
if __name__ == "__main__":
main()
نحوه استفاده
حالت تعاملی
python task_manager.py
حالت خط فرمان
# افزودن وظیفه
python task_manager.py add "تماس با مشتری"
python task_manager.py add "ارسال گزارش هفتگی"
# لیست همه وظایف
python task_manager.py list
# لیست فقط وظایف باز
python task_manager.py list --pending
# تکمیل وظیفه
python task_manager.py done 1
# حذف وظیفه
python task_manager.py remove 2
# آمار
python task_manager.py stats
# راهنما
python task_manager.py --help
python task_manager.py add --help
کد کامل پروژه
فایل کامل task_manager.py:
#!/usr/bin/env python3
"""
مدیر وظایف خط فرمان
نویسنده: محمدعلی ناظری - ICSD
"""
import json
import sys
import argparse
from pathlib import Path
from datetime import datetime
class Task:
def __init__(self, task_id, title, done=False, created_at=None):
self.task_id = task_id
self.title = title
self.done = done
self.created_at = created_at or datetime.now().isoformat()
def to_dict(self):
return {
"id": self.task_id,
"title": self.title,
"done": self.done,
"created_at": self.created_at
}
@classmethod
def from_dict(cls, data):
return cls(
task_id=data["id"],
title=data["title"],
done=data["done"],
created_at=data["created_at"]
)
def __str__(self):
status = "✓" if self.done else "○"
return f"[{status}] #{self.task_id} - {self.title}"
class TaskManager:
def __init__(self, filename="tasks.json"):
self.filename = Path(filename)
self.tasks = []
self.load()
def load(self):
if not self.filename.exists():
return
try:
with open(self.filename, "r", encoding="utf-8") as f:
data = json.load(f)
self.tasks = [Task.from_dict(t) for t in data]
except (json.JSONDecodeError, KeyError):
self.tasks = []
def save(self):
data = [t.to_dict() for t in self.tasks]
with open(self.filename, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def get_next_id(self):
if not self.tasks:
return 1
return max(t.task_id for t in self.tasks) + 1
def add(self, title):
if not title.strip():
raise ValueError("عنوان نمیتواند خالی باشد")
task = Task(self.get_next_id(), title.strip())
self.tasks.append(task)
self.save()
return task
def remove(self, task_id):
task = self.find(task_id)
if not task:
raise ValueError(f"وظیفه #{task_id} یافت نشد")
self.tasks.remove(task)
self.save()
return task
def complete(self, task_id):
task = self.find(task_id)
if not task:
raise ValueError(f"وظیفه #{task_id} یافت نشد")
task.done = True
self.save()
return task
def find(self, task_id):
return next((t for t in self.tasks if t.task_id == task_id), None)
def list_all(self, show_done=True):
if show_done:
return self.tasks
return [t for t in self.tasks if not t.done]
def stats(self):
total = len(self.tasks)
done = sum(1 for t in self.tasks if t.done)
return {"total": total, "done": done, "pending": total - done}
# (interactive_mode, cli_mode, main همانند بالا)
گسترش پروژه
پس از تکمیل پروژه پایه، میتوانید این قابلیتها را اضافه کنید:
- اولویت: کم/متوسط/زیاد به Task اضافه کنید
- تاریخ سررسید (deadline): با datetime و jdatetime
- دستهبندی (tags): هر وظیفه چند tag داشته باشد
- جستجو: در عنوان وظایف
- صادرات/واردات: CSV یا اکسل
- ذخیره در پایگاه داده: SQLite به جای JSON
- رابط گرافیکی: با Tkinter یا PyQt
- API وب: با FastAPI یا Flask
شما دوره پایتون مقدماتی را با موفقیت به پایان رساندید. حالا آماده یادگیری مفاهیم پیشرفتهتر در دوره پایتون پیشرفته هستید — جایی که با Decorators، Generators، Async/Await، Type Hints و طراحی پروژههای واقعی Python آشنا میشوید.
قدمهای بعدی
- دوره پایتون پیشرفته را شروع کنید
- برای ساخت وب با پایتون، دوره Django را ببینید
- برای کار با پایگاه داده، دوره MySQL یا PostgreSQL را مطالعه کنید
- پروژههای GitHub باز را مطالعه و در آنها مشارکت کنید
جمعبندی
- یک پروژه واقعی پایتون با همه مفاهیم دوره ساختیم
- از کلاس، فایل، JSON، Exception، argparse استفاده کردیم
- دو حالت تعاملی و خط فرمان پیادهسازی شد
- کد ماژولار و قابل گسترش است