۱۶

پروژه نهایی با پایتون عادی

ساخت یک سیستم مدیریت کتابخانه کامل با استفاده از تمام مفاهیم آموخته شده.

هدف پروژه

در این درس، یک سیستم مدیریت کتابخانه کامل می‌سازیم که شامل مدیریت کتاب‌ها، اعضا، امانت‌ها و گزارش‌گیری است. این پروژه تمام مفاهیم آموخته شده در دوره را به کار می‌گیرد.

ویژگی‌های اصلی:

  • • مدیریت کتاب‌ها (افزودن، حذف، جستجو)
  • • مدیریت اعضای کتابخانه
  • • سیستم امانت و بازگشت کتاب
  • • گزارش‌گیری و آمار

مفاهیم استفاده شده:

  • • کلاس‌ها و شیءگرایی
  • • فایل‌ها و JSON
  • • Exception Handling
  • • Regular Expressions

ساختار پروژه

ساختار فایل‌ها
library_management/
├── main.py              # فایل اصلی برنامه
├── models/
│   ├── book.py         # کلاس کتاب
│   ├── member.py       # کلاس عضو
│   └── loan.py         # کلاس امانت
├── managers/
│   ├── library_manager.py  # مدیر کتابخانه
│   └── file_manager.py     # مدیریت فایل‌ها
├── utils/
│   ├── validators.py   # اعتبارسنجی داده‌ها
│   └── helpers.py      # توابع کمکی
└── data/
    ├── books.json      # داده‌های کتاب‌ها
    ├── members.json    # داده‌های اعضا
    └── loans.json      # داده‌های امانت‌ها

مدل کتاب (Book)

models/book.py
from datetime import datetime
import re

class Book:
    def __init__(self, isbn, title, author, publisher, year, copies=1):
        self.isbn = self._validate_isbn(isbn)
        self.title = title.strip()
        self.author = author.strip()
        self.publisher = publisher.strip()
        self.year = self._validate_year(year)
        self.copies = max(1, copies)
        self.available_copies = copies
        self.created_at = datetime.now().isoformat()
    
    def _validate_isbn(self, isbn):
        """اعتبارسنجی شابک کتاب"""
        isbn_pattern = r'^\d{10}(\d{3})?$'
        clean_isbn = re.sub(r'[^\d]', '', isbn)
        
        if not re.match(isbn_pattern, clean_isbn):
            raise ValueError("شابک نامعتبر است")
        return clean_isbn
    
    def _validate_year(self, year):
        """اعتبارسنجی سال انتشار"""
        current_year = datetime.now().year
        if not (1000 <= year <= current_year):
            raise ValueError(f"سال انتشار باید بین 1000 تا {current_year} باشد")
        return year
    
    def is_available(self):
        """بررسی در دسترس بودن کتاب"""
        return self.available_copies > 0
    
    def borrow(self):
        """امانت دادن کتاب"""
        if not self.is_available():
            raise ValueError("کتاب در دسترس نیست")
        self.available_copies -= 1
    
    def return_book(self):
        """بازگشت کتاب"""
        if self.available_copies >= self.copies:
            raise ValueError("تمام نسخه‌های کتاب موجود است")
        self.available_copies += 1
    
    def to_dict(self):
        """تبدیل به دیکشنری برای ذخیره در JSON"""
        return {
            'isbn': self.isbn,
            'title': self.title,
            'author': self.author,
            'publisher': self.publisher,
            'year': self.year,
            'copies': self.copies,
            'available_copies': self.available_copies,
            'created_at': self.created_at
        }
    
    @classmethod
    def from_dict(cls, data):
        """ساخت شیء از دیکشنری"""
        book = cls(
            data['isbn'], data['title'], data['author'],
            data['publisher'], data['year'], data['copies']
        )
        book.available_copies = data.get('available_copies', data['copies'])
        book.created_at = data.get('created_at', datetime.now().isoformat())
        return book
    
    def __str__(self):
        return f"{self.title} - {self.author} ({self.year})"
    
    def __repr__(self):
        return f"Book(isbn='{self.isbn}', title='{self.title}')"

مدل عضو (Member)

models/member.py
from datetime import datetime
import re

class Member:
    def __init__(self, member_id, name, email, phone, address=""):
        self.member_id = str(member_id)
        self.name = name.strip()
        self.email = self._validate_email(email)
        self.phone = self._validate_phone(phone)
        self.address = address.strip()
        self.join_date = datetime.now().isoformat()
        self.borrowed_books = []  # لیست ISBN کتاب‌های امانت گرفته شده
        self.is_active = True
    
    def _validate_email(self, email):
        """اعتبارسنجی ایمیل"""
        email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(email_pattern, email):
            raise ValueError("ایمیل نامعتبر است")
        return email.lower()
    
    def _validate_phone(self, phone):
        """اعتبارسنجی شماره تلفن"""
        phone_pattern = r'^09\d{9}$'
        clean_phone = re.sub(r'[^\d]', '', phone)
        if not re.match(phone_pattern, clean_phone):
            raise ValueError("شماره تلفن نامعتبر است (باید با 09 شروع شود)")
        return clean_phone
    
    def can_borrow(self, max_books=3):
        """بررسی امکان امانت گرفتن کتاب جدید"""
        return len(self.borrowed_books) < max_books and self.is_active
    
    def borrow_book(self, isbn):
        """امانت گرفتن کتاب"""
        if not self.can_borrow():
            raise ValueError("امکان امانت گرفتن کتاب جدید وجود ندارد")
        if isbn in self.borrowed_books:
            raise ValueError("این کتاب قبلاً امانت گرفته شده")
        self.borrowed_books.append(isbn)
    
    def return_book(self, isbn):
        """بازگشت کتاب"""
        if isbn not in self.borrowed_books:
            raise ValueError("این کتاب توسط این عضو امانت گرفته نشده")
        self.borrowed_books.remove(isbn)
    
    def deactivate(self):
        """غیرفعال کردن عضویت"""
        if self.borrowed_books:
            raise ValueError("ابتدا تمام کتاب‌های امانتی را بازگردانید")
        self.is_active = False
    
    def to_dict(self):
        """تبدیل به دیکشنری"""
        return {
            'member_id': self.member_id,
            'name': self.name,
            'email': self.email,
            'phone': self.phone,
            'address': self.address,
            'join_date': self.join_date,
            'borrowed_books': self.borrowed_books,
            'is_active': self.is_active
        }
    
    @classmethod
    def from_dict(cls, data):
        """ساخت شیء از دیکشنری"""
        member = cls(
            data['member_id'], data['name'], data['email'],
            data['phone'], data.get('address', '')
        )
        member.join_date = data.get('join_date', datetime.now().isoformat())
        member.borrowed_books = data.get('borrowed_books', [])
        member.is_active = data.get('is_active', True)
        return member
    
    def __str__(self):
        return f"{self.name} ({self.member_id})"

مدیر کتابخانه (Library Manager)

managers/library_manager.py
from datetime import datetime, timedelta
from models.book import Book
from models.member import Member
from models.loan import Loan
from managers.file_manager import FileManager
from utils.validators import validate_search_query

class LibraryManager:
    def __init__(self, data_dir="data"):
        self.file_manager = FileManager(data_dir)
        self.books = self._load_books()
        self.members = self._load_members()
        self.loans = self._load_loans()
    
    def _load_books(self):
        """بارگذاری کتاب‌ها از فایل"""
        data = self.file_manager.load_books()
        return {book['isbn']: Book.from_dict(book) for book in data}
    
    def _load_members(self):
        """بارگذاری اعضا از فایل"""
        data = self.file_manager.load_members()
        return {member['member_id']: Member.from_dict(member) for member in data}
    
    def _load_loans(self):
        """بارگذاری امانت‌ها از فایل"""
        data = self.file_manager.load_loans()
        return [Loan.from_dict(loan) for loan in data]
    
    def save_all_data(self):
        """ذخیره تمام داده‌ها"""
        try:
            # ذخیره کتاب‌ها
            books_data = [book.to_dict() for book in self.books.values()]
            self.file_manager.save_books(books_data)
            
            # ذخیره اعضا
            members_data = [member.to_dict() for member in self.members.values()]
            self.file_manager.save_members(members_data)
            
            # ذخیره امانت‌ها
            loans_data = [loan.to_dict() for loan in self.loans]
            self.file_manager.save_loans(loans_data)
            
            print("✅ تمام داده‌ها با موفقیت ذخیره شدند")
        except Exception as e:
            print(f"❌ خطا در ذخیره داده‌ها: {e}")
    
    # مدیریت کتاب‌ها
    def add_book(self, isbn, title, author, publisher, year, copies=1):
        """افزودن کتاب جدید"""
        try:
            if isbn in self.books:
                # اگر کتاب موجود است، تعداد نسخه‌ها را افزایش دهیم
                self.books[isbn].copies += copies
                self.books[isbn].available_copies += copies
                print(f"✅ تعداد نسخه‌های کتاب '{title}' افزایش یافت")
            else:
                book = Book(isbn, title, author, publisher, year, copies)
                self.books[isbn] = book
                print(f"✅ کتاب '{title}' با موفقیت اضافه شد")
            self.save_all_data()
        except ValueError as e:
            print(f"❌ خطا: {e}")
    
    def search_books(self, query, search_type="title"):
        """جستجوی کتاب‌ها"""
        if not validate_search_query(query):
            return []
        
        query = query.lower().strip()
        results = []
        
        for book in self.books.values():
            if search_type == "title" and query in book.title.lower():
                results.append(book)
            elif search_type == "author" and query in book.author.lower():
                results.append(book)
            elif search_type == "isbn" and query in book.isbn:
                results.append(book)
        
        return results
    
    def get_book_info(self, isbn):
        """دریافت اطلاعات کتاب"""
        return self.books.get(isbn)
    
    # مدیریت اعضا
    def add_member(self, member_id, name, email, phone, address=""):
        """افزودن عضو جدید"""
        try:
            if member_id in self.members:
                print(f"❌ عضوی با شناسه {member_id} قبلاً ثبت شده")
                return False
            
            member = Member(member_id, name, email, phone, address)
            self.members[member_id] = member
            print(f"✅ عضو '{name}' با موفقیت اضافه شد")
            self.save_all_data()
            return True
        except ValueError as e:
            print(f"❌ خطا: {e}")
            return False
    
    def get_member_info(self, member_id):
        """دریافت اطلاعات عضو"""
        return self.members.get(str(member_id))
    
    # مدیریت امانت‌ها
    def borrow_book(self, member_id, isbn):
        """امانت دادن کتاب"""
        try:
            member = self.get_member_info(member_id)
            book = self.get_book_info(isbn)
            
            if not member:
                print(f"❌ عضوی با شناسه {member_id} یافت نشد")
                return False
            
            if not book:
                print(f"❌ کتابی با شابک {isbn} یافت نشد")
                return False
            
            # بررسی امکان امانت
            if not member.can_borrow():
                print("❌ این عضو نمی‌تواند کتاب جدید امانت بگیرد")
                return False
            
            if not book.is_available():
                print("❌ این کتاب در حال حاضر در دسترس نیست")
                return False
            
            # انجام امانت
            book.borrow()
            member.borrow_book(isbn)
            
            # ثبت امانت
            loan = Loan(member_id, isbn)
            self.loans.append(loan)
            
            print(f"✅ کتاب '{book.title}' به '{member.name}' امانت داده شد")
            self.save_all_data()
            return True
            
        except Exception as e:
            print(f"❌ خطا در امانت: {e}")
            return False
    
    def return_book(self, member_id, isbn):
        """بازگشت کتاب"""
        try:
            member = self.get_member_info(member_id)
            book = self.get_book_info(isbn)
            
            if not member or not book:
                print("❌ اطلاعات عضو یا کتاب یافت نشد")
                return False
            
            # پیدا کردن امانت فعال
            active_loan = None
            for loan in self.loans:
                if (loan.member_id == str(member_id) and 
                    loan.isbn == isbn and not loan.is_returned):
                    active_loan = loan
                    break
            
            if not active_loan:
                print("❌ امانت فعالی برای این کتاب و عضو یافت نشد")
                return False
            
            # بازگشت کتاب
            book.return_book()
            member.return_book(isbn)
            active_loan.return_book()
            
            print(f"✅ کتاب '{book.title}' با موفقیت بازگردانده شد")
            self.save_all_data()
            return True
            
        except Exception as e:
            print(f"❌ خطا در بازگشت: {e}")
            return False
    
    def get_overdue_loans(self, days=14):
        """دریافت امانت‌های معوقه"""
        overdue_date = datetime.now() - timedelta(days=days)
        overdue_loans = []
        
        for loan in self.loans:
            if (not loan.is_returned and 
                datetime.fromisoformat(loan.loan_date) < overdue_date):
                overdue_loans.append(loan)
        
        return overdue_loans
    
    def generate_report(self):
        """تولید گزارش کلی کتابخانه"""
        total_books = len(self.books)
        total_copies = sum(book.copies for book in self.books.values())
        available_copies = sum(book.available_copies for book in self.books.values())
        borrowed_copies = total_copies - available_copies
        
        total_members = len(self.members)
        active_members = sum(1 for member in self.members.values() if member.is_active)
        
        total_loans = len(self.loans)
        active_loans = sum(1 for loan in self.loans if not loan.is_returned)
        overdue_loans = len(self.get_overdue_loans())
        
        report = f"""
📊 گزارش کلی کتابخانه
{'='*30}
📚 کتاب‌ها:
   - تعداد کل کتاب‌ها: {total_books}
   - تعداد کل نسخه‌ها: {total_copies}
   - نسخه‌های موجود: {available_copies}
   - نسخه‌های امانتی: {borrowed_copies}

👥 اعضا:
   - تعداد کل اعضا: {total_members}
   - اعضای فعال: {active_members}

📋 امانت‌ها:
   - تعداد کل امانت‌ها: {total_loans}
   - امانت‌های فعال: {active_loans}
   - امانت‌های معوقه: {overdue_loans}
        """
        
        return report

برنامه اصلی

main.py
from managers.library_manager import LibraryManager
from utils.helpers import clear_screen, get_user_input, show_menu

def main():
    """تابع اصلی برنامه"""
    library = LibraryManager()
    
    print("🏛️  خوش آمدید به سیستم مدیریت کتابخانه")
    print("="*50)
    
    while True:
        try:
            clear_screen()
            choice = show_main_menu()
            
            if choice == '1':
                book_management_menu(library)
            elif choice == '2':
                member_management_menu(library)
            elif choice == '3':
                loan_management_menu(library)
            elif choice == '4':
                reports_menu(library)
            elif choice == '5':
                print("👋 خداحافظ!")
                break
            else:
                print("❌ گزینه نامعتبر")
                input("برای ادامه Enter بزنید...")
                
        except KeyboardInterrupt:
            print("\n👋 برنامه متوقف شد")
            break
        except Exception as e:
            print(f"❌ خطای غیرمنتظره: {e}")
            input("برای ادامه Enter بزنید...")

def show_main_menu():
    """نمایش منوی اصلی"""
    menu_options = [
        "📚 مدیریت کتاب‌ها",
        "👥 مدیریت اعضا", 
        "📋 مدیریت امانت‌ها",
        "📊 گزارش‌ها",
        "🚪 خروج"
    ]
    
    print("\n🏛️  منوی اصلی سیستم مدیریت کتابخانه")
    print("="*40)
    
    for i, option in enumerate(menu_options, 1):
        print(f"{i}. {option}")
    
    return input("\nانتخاب شما: ").strip()

def book_management_menu(library):
    """منوی مدیریت کتاب‌ها"""
    while True:
        clear_screen()
        print("📚 مدیریت کتاب‌ها")
        print("="*20)
        print("1. افزودن کتاب")
        print("2. جستجوی کتاب")
        print("3. مشاهده اطلاعات کتاب")
        print("4. بازگشت به منوی اصلی")
        
        choice = input("\nانتخاب شما: ").strip()
        
        if choice == '1':
            add_book_interface(library)
        elif choice == '2':
            search_books_interface(library)
        elif choice == '3':
            view_book_interface(library)
        elif choice == '4':
            break
        else:
            print("❌ گزینه نامعتبر")
            input("برای ادامه Enter بزنید...")

def add_book_interface(library):
    """رابط افزودن کتاب"""
    print("\n➕ افزودن کتاب جدید")
    print("-"*20)
    
    try:
        isbn = input("شابک کتاب: ").strip()
        title = input("عنوان کتاب: ").strip()
        author = input("نویسنده: ").strip()
        publisher = input("ناشر: ").strip()
        year = int(input("سال انتشار: "))
        copies = int(input("تعداد نسخه‌ها (پیش‌فرض: 1): ") or "1")
        
        library.add_book(isbn, title, author, publisher, year, copies)
        
    except ValueError as e:
        print(f"❌ خطا در ورودی: {e}")
    except Exception as e:
        print(f"❌ خطای غیرمنتظره: {e}")
    
    input("\nبرای ادامه Enter بزنید...")

def search_books_interface(library):
    """رابط جستجوی کتاب‌ها"""
    print("\n🔍 جستجوی کتاب")
    print("-"*15)
    
    search_type = input("نوع جستجو (title/author/isbn): ").strip().lower()
    if search_type not in ['title', 'author', 'isbn']:
        search_type = 'title'
    
    query = input(f"جستجو بر اساس {search_type}: ").strip()
    
    if not query:
        print("❌ متن جستجو نمی‌تواند خالی باشد")
        input("برای ادامه Enter بزنید...")
        return
    
    results = library.search_books(query, search_type)
    
    if results:
        print(f"\n✅ {len(results)} کتاب یافت شد:")
        print("-"*50)
        for i, book in enumerate(results, 1):
            print(f"{i}. {book.title} - {book.author}")
            print(f"   شابک: {book.isbn} | موجود: {book.available_copies}/{book.copies}")
            print()
    else:
        print("❌ کتابی یافت نشد")
    
    input("برای ادامه Enter بزنید...")

if __name__ == "__main__":
    main()

تمرین عملی

تمرین 1: گسترش سیستم

ویژگی‌های زیر را به سیستم اضافه کنید:

  • • سیستم رزرو کتاب
  • • مدیریت جریمه برای تأخیر
  • • دسته‌بندی کتاب‌ها
  • • سیستم امتیازدهی به کتاب‌ها

راه‌حل تمرین 1:

# اضافه کردن کلاس رزرو
class Reservation:
    def __init__(self, member_id, isbn):
        self.member_id = str(member_id)
        self.isbn = isbn
        self.reservation_date = datetime.now().isoformat()
        self.is_active = True
        self.expiry_date = (datetime.now() + timedelta(days=3)).isoformat()
    
    def is_expired(self):
        return datetime.now() > datetime.fromisoformat(self.expiry_date)

# اضافه کردن به LibraryManager
def reserve_book(self, member_id, isbn):
    """رزرو کتاب"""
    member = self.get_member_info(member_id)
    book = self.get_book_info(isbn)
    
    if not member or not book:
        return False
    
    if book.is_available():
        print("کتاب در دسترس است، می‌توانید مستقیماً امانت بگیرید")
        return False
    
    # بررسی رزرو قبلی
    for reservation in self.reservations:
        if (reservation.member_id == str(member_id) and 
            reservation.isbn == isbn and reservation.is_active):
            print("شما قبلاً این کتاب را رزرو کرده‌اید")
            return False
    
    reservation = Reservation(member_id, isbn)
    self.reservations.append(reservation)
    print(f"کتاب '{book.title}' برای شما رزرو شد")
    return True

تمرین 2: رابط گرافیکی

با استفاده از tkinter یک رابط گرافیکی ساده برای سیستم بسازید.

راه‌حل تمرین 2:

import tkinter as tk
from tkinter import ttk, messagebox
from managers.library_manager import LibraryManager

class LibraryGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("سیستم مدیریت کتابخانه")
        self.root.geometry("800x600")
        
        self.library = LibraryManager()
        self.create_widgets()
    
    def create_widgets(self):
        # ایجاد تب‌ها
        notebook = ttk.Notebook(self.root)
        notebook.pack(fill='both', expand=True, padx=10, pady=10)
        
        # تب کتاب‌ها
        books_frame = ttk.Frame(notebook)
        notebook.add(books_frame, text='کتاب‌ها')
        self.create_books_tab(books_frame)
        
        # تب اعضا
        members_frame = ttk.Frame(notebook)
        notebook.add(members_frame, text='اعضا')
        self.create_members_tab(members_frame)
    
    def create_books_tab(self, parent):
        # فرم افزودن کتاب
        add_frame = ttk.LabelFrame(parent, text="افزودن کتاب")
        add_frame.pack(fill='x', padx=5, pady=5)
        
        ttk.Label(add_frame, text="شابک:").grid(row=0, column=0, sticky='w')
        self.isbn_entry = ttk.Entry(add_frame)
        self.isbn_entry.grid(row=0, column=1, sticky='ew')
        
        ttk.Label(add_frame, text="عنوان:").grid(row=1, column=0, sticky='w')
        self.title_entry = ttk.Entry(add_frame)
        self.title_entry.grid(row=1, column=1, sticky='ew')
        
        ttk.Button(add_frame, text="افزودن", 
                  command=self.add_book).grid(row=2, column=1, sticky='e')
        
        add_frame.columnconfigure(1, weight=1)
    
    def add_book(self):
        isbn = self.isbn_entry.get().strip()
        title = self.title_entry.get().strip()
        
        if not isbn or not title:
            messagebox.showerror("خطا", "لطفاً تمام فیلدها را پر کنید")
            return
        
        try:
            # فرض می‌کنیم سایر فیلدها را هم دریافت کرده‌ایم
            self.library.add_book(isbn, title, "نویسنده", "ناشر", 2023)
            messagebox.showinfo("موفقیت", "کتاب با موفقیت اضافه شد")
            self.clear_book_entries()
        except Exception as e:
            messagebox.showerror("خطا", str(e))
    
    def clear_book_entries(self):
        self.isbn_entry.delete(0, tk.END)
        self.title_entry.delete(0, tk.END)

if __name__ == "__main__":
    root = tk.Tk()
    app = LibraryGUI(root)
    root.mainloop()

خلاصه پروژه

مفاهیم استفاده شده:

  • کلاس‌ها و شیءگرایی
  • مدیریت فایل‌ها و JSON
  • Exception Handling
  • Regular Expressions
  • تاریخ و زمان

ویژگی‌های پیاده‌سازی شده:

  • مدیریت کامل کتاب‌ها
  • مدیریت اعضای کتابخانه
  • سیستم امانت و بازگشت
  • جستجوی پیشرفته
  • گزارش‌گیری و آمار

🎉 تبریک! شما با موفقیت یک سیستم مدیریت کتابخانه کامل با پایتون ساختید. این پروژه نشان‌دهنده تسلط شما بر مفاهیم مختلف برنامه‌نویسی پایتون است.