المشروع النهائي: إدارة درجات الطلاب (الجزء 1)
هذا هو المشروع الختامي لدورة بايثون التعليمية. على مدى ثلاثة دروس، سنبني نظاماً كاملاً لإدارة درجات الطلاب من الصفر. للمشروع ثلاث طبقات: طبقة البيانات (استمرار JSON)، طبقة المنطق (معالجة الدرجات)، وطبقة العرض (واجهة سطر الأوامر). هذا الدرس يبني الأساس — المتطلبات وطبقة البيانات.
نظرة عامة على المشروع
نظام إدارة درجات الطلاب
├── الدرس 34: طبقة البيانات — تحليل المتطلبات + استمرار JSON/CSV
├── الدرس 35: طبقة المنطق — إحصائيات الدرجات + الترتيب + معالجة الاستثناءات
└── الدرس 36: طبقة العرض — واجهة القائمة + البحث + التقارير
مواصفات المتطلبات
1. إدارة الطلاب
├── إضافة طالب (الاسم، رقم الطالب، الفصل)
└── حذف طالب (برقم الطالب)
2. إدارة الدرجات
├── إدخال الدرجات (برقم الطالب لمواد متعددة)
├── تعديل الدرجات
└── حذف سجلات الدرجات
3. الإحصائيات والتحليل
├── بطاقة تقرير فردية
├── ترتيب الفصل
├── متوسطات المواد
└── توزيع الدرجات
4. استمرارية البيانات
├── تخزين البيانات بتنسيق JSON في ملف
└── تحميل تلقائي عند بدء التشغيل
تصميم هيكل البيانات
مثال: هيكل بيانات JSON
# البيانات مخزنة بتنسيق JSON
{
"students": {
"2024001": {
"name": "Zhang San",
"class_name": "Class 1",
"scores": {
"Chinese": 85,
"Math": 92,
"English": 78
}
},
"2024002": {
"name": "Li Si",
"class_name": "Class 1",
"scores": {
"Chinese": 90,
"Math": 88,
"English": 95
}
}
},
"subjects": ["Chinese", "Math", "English"],
"classes": ["Class 1", "Class 2"]
}
تنفيذ طبقة البيانات
مثال: تنفيذ طبقة البيانات واختبارها
import json
import os
DATA_FILE = "grade_system.json"
# ====== هيكل البيانات الافتراضي ======
DEFAULT_DATA = {
"students": {},
"subjects": ["Chinese", "Math", "English"],
"classes": []
}
# ====== طبقة البيانات ======
def load_data():
"""حمّل البيانات من ملف JSON"""
if not os.path.exists(DATA_FILE):
save_data(DEFAULT_DATA.copy())
return DEFAULT_DATA.copy()
with open(DATA_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_data(data):
"""احفظ البيانات إلى ملف JSON"""
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# ====== إدارة الطلاب ======
def add_student(student_id, name, class_name):
"""أضف طالباً"""
data = load_data()
if student_id in data["students"]:
return False, f"Student ID {student_id} already exists!"
data["students"][student_id] = {
"name": name,
"class_name": class_name,
"scores": {}
}
if class_name not in data["classes"]:
data["classes"].append(class_name)
save_data(data)
return True, f"Added student: {name} ({student_id})"
def delete_student(student_id):
"""احذف طالباً"""
data = load_data()
if student_id not in data["students"]:
return False, f"Student ID {student_id} does not exist!"
name = data["students"][student_id]["name"]
del data["students"][student_id]
save_data(data)
return True, f"Deleted student: {name}"
def get_student(student_id):
"""احصل على معلومات طالب واحد"""
data = load_data()
return data["students"].get(student_id)
def get_all_students():
"""احصل على جميع الطلاب"""
data = load_data()
return data["students"]
# ====== إدارة الدرجات ======
def add_score(student_id, subject, score):
"""أدخل درجة"""
data = load_data()
if student_id not in data["students"]:
return False, f"Student ID {student_id} does not exist!"
if subject not in data["subjects"]:
return False, f"Subject {subject} does not exist!"
if not isinstance(score, (int, float)) or score < 0 or score > 100:
return False, "Score must be between 0 and 100!"
data["students"][student_id]["scores"][subject] = score
save_data(data)
return True, f"Entered {data['students'][student_id]['name']}'s {subject} score: {score}"
def update_score(student_id, subject, score):
"""حدّث درجة (نفس وظيفة add_score)"""
return add_score(student_id, subject, score)
def delete_score(student_id, subject):
"""احذف درجة لمادة"""
data = load_data()
if student_id not in data["students"]:
return False, f"Student ID {student_id} does not exist!"
if subject not in data["students"][student_id]["scores"]:
return False, f"{data['students'][student_id]['name']} has no {subject} score"
del data["students"][student_id]["scores"][subject]
save_data(data)
return True, f"Deleted {data['students'][student_id]['name']}'s {subject} score"
# ====== كود الاختبار ======
if __name__ == "__main__":
print("=== Testing Data Layer ===")
print(add_student("2024001", "Zhang San", "Class 1"))
print(add_student("2024002", "Li Si", "Class 1"))
print(add_student("2024003", "Wang Wu", "Class 2"))
print(add_score("2024001", "Chinese", 85))
print(add_score("2024001", "Math", 92))
print(add_score("2024002", "Chinese", 90))
student = get_student("2024001")
print(f"\nZhang San's info: {student}")
print(f"\nTotal students: {len(get_all_students())}")
أبرز ملامح التصميم
- إرجاع دالة موحد: كل دالة تُرجع صفاً
(bool, str)—Trueتعني نجاح،Falseتعني فشل، والرسالة النصية. - تغليف العمليات المتكررة: دوال
load_data()وsave_data()يُعاد استخدامها عبر جميع عمليات البيانات. - التحقق من صحة الإدخال: فحص نطاق الدرجة ووجود رقم الطالب يتم في طبقة البيانات، لذا لا يحتاج المتصلون في الطبقات العليا لتكرارها.
- جاهز للتوسيع: الدرسان 35 و 36 سيضيفان وظائف فوق طبقة البيانات هذه.
❓ أسئلة شائعة
os.path.join() لبناء المسارات وتجنب الترميز الثابت.
س:لماذا إرجاع صف (success, message) بدلاً من إثارة استثناءات؟ ج:هذا هو "نمط رمز الحالة" — يمكن للمتصل أن يقرر ما يفعله بناءً على success، وهو أخف من try/except. مناسب لأدوات CLI الصغيرة؛ للمشاريع الأكبر، يُوصى بفئات استثناء مخصصة.📖 ملخص
- المشروع يستخدم عمارة ثلاثية الطبقات: طبقة البيانات ← طبقة المنطق ← طبقة العرض؛ هذا الدرس يكمل طبقة البيانات
- البيانات مُستمرة بتنسيق JSON؛ أرقام الطلاب تعمل كمفاتيح قاموس لبحث O(1)
- الدرجات تتراوح من 0 إلى 100، مع التحقق عند الإدخال
- جميع دوال العمليات تُرجع صفوف
(success, message)لسهولة معالجة المتصل
📝 تمارين
-
مبتدئ (الصعوبة ⭐): شغّل كود طبقة البيانات أعلاه، أضف 2-3 طلاب بدرجاتهم، وتحقق من إنشاء ملف JSON بشكل صحيح.
-
متوسط (الصعوبة ⭐⭐): أضف دالة
update_student(student_id, name=None, class_name=None)إلى طبقة البيانات التي تُحدّث فقط الحقول المُقدّمة (تترك الأخرى دون تغيير). -
متقدم (الصعوبة ⭐⭐⭐): أضف وظيفة إدخال دفعة إلى
add_score— اقبل قاموساً مثل{"Chinese": 85, "Math": 92}لإدخال درجات متعددة مرة واحدة. متطلب: إذا كانت أي درجة غير صالحة، تراجع عن عملية الدفعة بأكملها (لا تحفظ أي بيانات).



