~/icsd.ir — bash
SYSTEM_ONLINE

پروژه نهایی – مدیر وظایف خط فرمان

در این فصل پایانی، با کنار هم گذاشتن همه چیزهایی که در این دوره یاد گرفتیم، یک پروژه واقعی می‌سازیم: مدیر وظایف خط فرمان (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 استفاده کردیم
  • دو حالت تعاملی و خط فرمان پیاده‌سازی شد
  • کد ماژولار و قابل گسترش است

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.