Python: مشروع: نظام إدارة بطاقات العمل

هذا هو المشروع الختامي للمرحلة الثانية. كل المعرفة من الدروس السابقة — القوائم، القواميس، الشاملات، طرق السلاسل النصية — ستُستخدم لبناء نظام إدارة بطاقات عمل حقيقي قابل للاستخدام في سطر الأوامر. إكمال هذا المشروع يعني أنك أتقنت حقاً مفاهيم الحاويات الأساسية في بايثون.


1. متطلبات المشروع

سنبني "مدير بطاقات عمل" يعمل في الطرفية ويدعم الميزات التالية:

TEXT 📖 للعرض فقط
1. إضافة بطاقة — أدخل الاسم، الهاتف، البريد الإلكتروني
2. عرض جميع البطاقات — اعرضها بتنسيق جدول
3. البحث عن بطاقات — ابحث بالاسم (يدعم البحث الضبابي)
4. تعديل بطاقة — حدّث المعلومات بالاسم
5. حذف بطاقة — احذف بالاسم
6. الإحصائيات — أظهر العدد الإجمالي، إحصائيات نطاق البريد الإلكتروني
7. الخروج من البرنامج

تُخزّن البيانات باستخدام قائمة + قاموس: جميع البطاقات في قائمة، كل بطاقة كقاموس.



2. تصميم هيكل البيانات

▶ مثال: هيكل بيانات بطاقة العمل

PYTHON
# كل بطاقة هي قاموس
# جميع البطاقات مخزنة في قائمة
[
    {"name": "Zhang San", "phone": "13800138000", "email": "zhangsan@example.com"},
    {"name": "Li Si", "phone": "13900139000", "email": "lisi@test.com"},
]
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
# Executed successfully


3. الكود الكامل

احفظ هذا الملف باسم card_manager.py وشغّله مباشرة:

▶ مثال: نظام إدارة بطاقات العمل

TEXTN 📖 للعرض فقط
# ====== نظام إدارة بطاقات العمل ======

# تخزين البيانات
cards = []

def show_menu():
    """اعرض القائمة الرئيسية"""
    print("\n" + "=" * 35)
    print("Business Card Management System")
    print("=" * 35)
    print("1. Add Card")
    print("2. List All Cards")
    print("3. Search Cards")
    print("4. Modify Card")
    print("5. Delete Card")
    print("6. Statistics")
    print("7. Exit")
    print("=" * 35)


def add_card():
    """أضف بطاقة عمل"""
    print("\n--- Add Card ---")
    name = input("Name: ").strip()
    if not name:
        print("Name cannot be empty!")
        return

    phone = input("Phone: ").strip()
    email = input("Email: ").strip()

    for card in cards:
        if card["name"] == name:
            print(f"A card named {name} already exists. Cannot add duplicate.")
            return

    cards.append({"name": name, "phone": phone, "email": email})
    print(f"Card for {name} added.")


def list_cards():
    """اعرض جميع البطاقات"""
    if not cards:
        print("\nNo cards yet.")
        return

    print("\n--- All Cards ---")
    print(f"{'#':<4}{'Name':<10}{'Phone':<15}{'Email':<25}")
    print("-" * 54)

    for i, card in enumerate(cards, 1):
        print(f"{i:<4}{card['name']:<10}{card['phone']:<15}{card['email']:<25}")

    print(f"\nTotal: {len(cards)} cards")


def search_card():
    """ابحث عن بطاقات (يدعم البحث الضبابي)"""
    if not cards:
        print("\nNo cards yet.")
        return

    keyword = input("\nEnter name to search (partial match supported): ").strip()
    if not keyword:
        print("Please enter a keyword!")
        return

    results = [card for card in cards if keyword in card["name"]]

    if not results:
        print(f"No cards found matching '{keyword}'.")
        return

    print(f"\n--- Found {len(results)} cards ---")
    for card in results:
        print(f"Name: {card['name']}")
        print(f"Phone: {card['phone']}")
        print(f"Email: {card['email']}")
        print("-" * 20)


def modify_card():
    """عدّل بطاقة"""
    if not cards:
        print("\nNo cards yet.")
        return

    name = input("\nEnter the name to modify: ").strip()
    for card in cards:
        if card["name"] == name:
            print(f"Found card for {name}. Enter new info (press Enter to keep current):")

            new_name = input(f"Name ({card['name']}): ").strip()
            new_phone = input(f"Phone ({card['phone']}): ").strip()
            new_email = input(f"Email ({card['email']}): ").strip()

            if new_name:
                card["name"] = new_name
            if new_phone:
                card["phone"] = new_phone
            if new_email:
                card["email"] = new_email

            print(f"Card for {name} has been updated.")
            return

    print(f"No card found for {name}.")


def delete_card():
    """احذف بطاقة"""
    if not cards:
        print("\nNo cards yet.")
        return

    name = input("\nEnter the name to delete: ").strip()
    for i, card in enumerate(cards):
        if card["name"] == name:
            confirm = input(f"Are you sure you want to delete {name}'s card? (y/n): ")
            if confirm.lower() == "y":
                cards.pop(i)
                print(f"Card for {name} has been deleted.")
            else:
                print("Deletion cancelled.")
            return

    print(f"No card found for {name}.")


def show_stats():
    """اعرض إحصائيات البيانات"""
    if not cards:
        print("\nNo cards yet.")
        return

    print("\n--- Statistics ---")
    print(f"Total cards: {len(cards)}")

    domains = {}
    for card in cards:
        email = card["email"]
        if "@" in email:
            domain = email.split("@")[1]
            domains[domain] = domains.get(domain, 0) + 1

    if domains:
        print("\nEmail domain distribution:")
        for domain, count in sorted(domains.items(), key=lambda x: x[1], reverse=True):
            print(f"  {domain}: {count}")

    has_phone = sum(1 for card in cards if card["phone"])
    has_email = sum(1 for card in cards if card["email"])
    print(f"\nWith phone: {has_phone}")
    print(f"With email: {has_email}")


# ====== البرنامج الرئيسي ======
print("Welcome to the Business Card Management System!")
print("Enter the corresponding number to select a function.")

while True:
    show_menu()
    choice = input("Select option (1-7): ").strip()

    if choice == "1":
        add_card()
    elif choice == "2":
        list_cards()
    elif choice == "3":
        search_card()
    elif choice == "4":
        modify_card()
    elif choice == "5":
        delete_card()
    elif choice == "6":
        show_stats()
    elif choice == "7":
        print("Thank you for using! Goodbye!")
        break
    else:
        print("Invalid choice. Please enter a number between 1 and 7.")
139 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

الناتج:

TEXT 📖 للعرض فقط
// Executed successfully

نهج تصميم الكود: كل ميزة مُنفّذة كدالة مستقلة. البرنامج الرئيسي يستخدم حلقة while + if-elif للتوزيع. هذا يجعل هيكل الكود واضحاً — إضافة ميزة جديدة تتطلب فقط دالة جديدة وفرع elif.



4. اقتراحات التوسيع

▶ مثال: البحث عن البطاقات والإحصائيات (الصعوبة ⭐⭐)

PYTHON
cards = [
    {"name": "Alice", "phone": "13800138000", "email": "alice@example.com"},
    {"name": "Bob", "phone": "13900139000", "email": "bob@test.com"},
    {"name": "Alice Chen", "phone": "13700137000", "email": "alice.c@work.com"},
]

results = [c for c in cards if "Alice" in c["name"]]
print(f"Found {len(results)} matching 'Alice':")
for c in results:
    print(f"  {c['name']} - {c['phone']}")

domains = {}
for c in cards:
    if "@" in c["email"]:
        domain = c["email"].split("@")[1]
        domains[domain] = domains.get(domain, 0) + 1

print("\nEmail domain distribution:")
for d, count in sorted(domains.items(), key=lambda x: x[1], reverse=True):
    print(f"  {d}: {count}")

phones = [c["phone"] for c in cards]
has_dup = len(phones) != len(set(phones))
print(f"\nDuplicate phones: {has_dup}")
▶ جرّب الكود

المخرجات:

TEXT 📖 للعرض فقط
Found 2 matching 'Alice':
  Alice - 13800138000
  Alice Chen - 13700137000

Email domain distribution:
  example.com: 1
  test.com: 1
  work.com: 1

Duplicate phones: False


4. حالات الاستخدام الشائعة



❓ أسئلة شائعة

س لماذا استخدام قائمة + قاموس بدلاً من عمليات الملفات المباشرة؟
ج هذا المشروع يركز على عمليات الحاويات — القائمة تخزن جميع البطاقات، القواميس تخزن البطاقات الفردية. هذا يتيح لك تمرين CRUD على القوائم وعمليات المفتاح-القيمة في القواميس. إدخال/إخراج الملفات (الاستمرارية) سيُغطى في المرحلة 3. أتقن الحاويات أولاً، ثم عمليات الملفات — نهج خطوة بخطوة.
س تُفقد البيانات عند إغلاق البرنامج. ماذا أفعل؟
ج هذا طبيعي — بياناتنا مخزنة في الذاكرة، التي تُحرر عند انتهاء البرنامج. لاستمرار البيانات، بعد تعلم عمليات الملفات ووحدة JSON، استخدم json.dump(cards, open("data.json", "w")) للحفظ على القرص.
س البحث الضبابي يستخدم الكلمة المفتاحية in. هل هو حساس لحالة الأحرف؟
ج نعم. "Zhang" in "Zhang San" هو True، لكن "zhang" in "Zhang" هو False (حساس لحالة الأحرف). إذا كنت تحتاج بحثاً غير حساس لحالة الأحرف، حوّل إلى أحرف صغيرة قبل المقارنة: keyword.lower() in card["name"].lower().

📖 ملخص


📝 تمارين

  1. مبتدئ (الصعوبة ⭐): أضف ميزة جديدة لنظام البطاقات — عرض البطاقات مرتبة حسب الاسم (باستخدام sorted() مع معامل key).

  2. متوسط (الصعوبة ⭐⭐): أضف ميزة "إدارة المجموعات" لنظام البطاقات. أضف حقل group لكل بطاقة ("زملاء"، "أصدقاء"، "عائلة")، ثم نفذ خيار قائمة لعرض البطاقات حسب المجموعة.

  3. متقدم (الصعوبة ⭐⭐⭐): اكتب "نظام إدارة كتب" من الصفر دون الرجوع إلى الكود أعلاه. تشمل الميزات: إضافة كتاب (العنوان، المؤلف، السنة)، عرض الكل، البحث عن طريق المؤلف، الترتيب حسب السنة، عد الكتب حسب المؤلف. استخدم قائمة + قاموس للتخزين، كلها تعمل في سطر الأوامر.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%