Dự Án Tổng Hợp — Quản Lý Sinh Viên
Giai đoạn 3 – Nâng Cao & Thực Hành · Mục tiêu: Tổng hợp mọi kiến thức để xây dựng ứng dụng hoàn chỉnh.
1. Giới thiệu dự án
Xây dựng chương trình Quản lý Sinh viên với CRUD, lưu trữ JSON.
Tính năng & Kiến thức áp dụng
| Tính năng | Kiến thức |
|---|---|
| Thêm sinh viên | OOP, input, validation |
| Hiển thị danh sách | List, format, sort |
| Tìm kiếm | String methods, filter |
| Cập nhật / Xóa | Dict, exception handling |
| Thống kê | Hàm tổng hợp, comprehension |
| Lưu / đọc file | JSON, file I/O |
| Xuất báo cáo | CSV, formatting |
Cấu trúc thư mục
quan_ly_sv/
├── main.py
├── models/
│ ├── __init__.py
│ └── sinh_vien.py
├── services/
│ ├── __init__.py
│ └── sv_service.py
├── utils/
│ ├── __init__.py
│ ├── file_helper.py
│ └── validator.py
└── data/
└── sinh_vien.json
2. Code chi tiết
2.1. Class SinhVien
# models/sinh_vien.py
class SinhVien:
"""Đại diện cho một sinh viên."""
def __init__(self, ma_sv, ho_ten, lop,
diem_toan, diem_ly, diem_hoa):
self.ma_sv = ma_sv
self.ho_ten = ho_ten
self.lop = lop
self.diem_toan = diem_toan
self.diem_ly = diem_ly
self.diem_hoa = diem_hoa
@property
def diem_tb(self):
return round((self.diem_toan + self.diem_ly + self.diem_hoa) / 3, 2)
@property
def xep_loai(self):
if any(d < 3 for d in [self.diem_toan, self.diem_ly, self.diem_hoa]):
return "Yếu "
dtb = self.diem_tb
if dtb >= 9: return "Xuất sắc "
if dtb >= 8: return "Giỏi "
if dtb >= 6.5: return "Khá "
if dtb >= 5: return "TB "
return "Yếu "
def to_dict(self):
return {
"ma_sv": self.ma_sv,
"ho_ten": self.ho_ten,
"lop": self.lop,
"diem_toan": self.diem_toan,
"diem_ly": self.diem_ly,
"diem_hoa": self.diem_hoa,
}
@classmethod
def from_dict(cls, data):
return cls(**data)
def __str__(self):
return (f"| {self.ma_sv:^8} | {self.ho_ten:<20} | {self.lop:^6} | "
f"{self.diem_toan:>5} | {self.diem_ly:>5} | {self.diem_hoa:>5} | "
f"{self.diem_tb:>6} | {self.xep_loai:<13} |")
2.2. File Helper
# utils/file_helper.py
import json
import csv
import os
DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data")
JSON_PATH = os.path.join(DATA_DIR, "sinh_vien.json")
def doc_du_lieu():
if not os.path.exists(JSON_PATH):
return []
try:
with open(JSON_PATH, "r", encoding="utf-8") as f:
return json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
return []
def luu_du_lieu(ds):
os.makedirs(DATA_DIR, exist_ok=True)
with open(JSON_PATH, "w", encoding="utf-8") as f:
json.dump(ds, f, ensure_ascii=False, indent=2)
def xuat_csv(ds, file_path):
if not ds:
return
with open(file_path, "w", encoding="utf-8-sig", newline="") as f:
fields = ["ma_sv", "ho_ten", "lop", "diem_toan",
"diem_ly", "diem_hoa", "diem_tb", "xep_loai"]
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
writer.writerows(ds)
2.3. Validator
# utils/validator.py
def nhap_so(prompt, min_val=None, max_val=None):
while True:
try:
val = float(input(prompt))
if min_val is not None and val < min_val:
print(f" Phải >= {min_val}")
continue
if max_val is not None and val > max_val:
print(f" Phải <= {max_val}")
continue
return val
except ValueError:
print(" Nhập số hợp lệ!")
def nhap_chuoi(prompt, min_len=1):
while True:
val = input(prompt).strip()
if len(val) >= min_len:
return val
print(f" Nhập ít nhất {min_len} ký tự!")
2.4. Service
# services/sv_service.py
from models.sinh_vien import SinhVien
from utils.file_helper import doc_du_lieu, luu_du_lieu, xuat_csv
from utils.validator import nhap_so, nhap_chuoi
class SVService:
def __init__(self):
self.ds_sv = []
self._tai_du_lieu()
def _tai_du_lieu(self):
self.ds_sv = [SinhVien.from_dict(d) for d in doc_du_lieu()]
def _luu(self):
luu_du_lieu([sv.to_dict() for sv in self.ds_sv])
def _tim_theo_ma(self, ma):
for sv in self.ds_sv:
if sv.ma_sv.upper() == ma.upper():
return sv
return None
def them_sv(self):
print("\n THÊM SINH VIÊN")
ma = nhap_chuoi("Mã SV: ").upper()
if self._tim_theo_ma(ma):
print(f" Mã {ma} đã tồn tại!")
return
ten = nhap_chuoi("Họ tên: ").title()
lop = nhap_chuoi("Lớp: ").upper()
toan = nhap_so("Điểm Toán (0-10): ", 0, 10)
ly = nhap_so("Điểm Lý (0-10): ", 0, 10)
hoa = nhap_so("Điểm Hóa (0-10): ", 0, 10)
self.ds_sv.append(SinhVien(ma, ten, lop, toan, ly, hoa))
self._luu()
print(f" Đã thêm {ten} ({ma})")
def hien_thi_ds(self):
if not self.ds_sv:
print("\n Chưa có sinh viên!")
return
self._in_bang(" DANH SÁCH SINH VIÊN", self.ds_sv)
def _in_bang(self, tieu_de, ds):
ke = "+" + "-"*10 + "+" + "-"*22 + "+" + "-"*8 + "+" + \
"-"*7 + "+" + "-"*7 + "+" + "-"*7 + "+" + "-"*8 + "+" + "-"*15 + "+"
print(f"\n{tieu_de}")
print(ke)
print(f"| {'Mã SV':^8} | {'Họ tên':<20} | {'Lớp':^6} | "
f"{'Toán':>5} | {'Lý':>5} | {'Hóa':>5} | "
f"{'ĐTB':>6} | {'Xếp loại':<13} |")
print(ke)
for sv in ds:
print(sv)
print(ke)
print(f" Tổng: {len(ds)} sinh viên")
def tim_kiem(self):
print("\n TÌM KIẾM")
print("1. Theo mã SV")
print("2. Theo tên")
print("3. Theo lớp")
chon = input("Chọn: ").strip()
if chon == "1":
ma = nhap_chuoi("Mã SV: ").upper()
sv = self._tim_theo_ma(ma)
if sv:
self._in_bang(f" Kết quả: {ma}", [sv])
else:
print(f" Không tìm thấy {ma}")
elif chon == "2":
kw = nhap_chuoi("Tên: ").lower()
kq = [sv for sv in self.ds_sv if kw in sv.ho_ten.lower()]
if kq:
self._in_bang(f" Kết quả: '{kw}'", kq)
else:
print(f" Không tìm thấy '{kw}'")
elif chon == "3":
lop = nhap_chuoi("Lớp: ").upper()
kq = [sv for sv in self.ds_sv if sv.lop.upper() == lop]
if kq:
self._in_bang(f" Lớp {lop}", kq)
else:
print(f" Không có SV lớp {lop}")
def xoa_sv(self):
ma = nhap_chuoi("\n Mã SV cần xóa: ").upper()
sv = self._tim_theo_ma(ma)
if not sv:
print(f" Không tìm thấy {ma}")
return
xn = input(f"Xóa {sv.ho_ten}? (y/n): ").strip().lower()
if xn == "y":
self.ds_sv.remove(sv)
self._luu()
print(f" Đã xóa {sv.ho_ten}")
def thong_ke(self):
if not self.ds_sv:
print("\n Chưa có dữ liệu!")
return
dtb = [sv.diem_tb for sv in self.ds_sv]
xl = [sv.xep_loai for sv in self.ds_sv]
print("\n THỐNG KÊ")
print("=" * 40)
print(f"Tổng SV: {len(self.ds_sv)}")
print(f"ĐTB cao: {max(dtb)}")
print(f"ĐTB thấp: {min(dtb)}")
print(f"ĐTB chung: {sum(dtb)/len(dtb):.2f}")
print("\n Phân loại:")
for loai in ["Xuất sắc ", "Giỏi ", "Khá ", "TB ", "Yếu "]:
sl = xl.count(loai)
pt = sl / len(self.ds_sv) * 100
print(f" {loai:<15} {sl:>3} ({pt:>5.1f}%) {'█' * int(pt/5)}")
def xuat_bao_cao(self):
if not self.ds_sv:
print("\n Chưa có dữ liệu!")
return
data = []
for sv in self.ds_sv:
d = sv.to_dict()
d["diem_tb"] = sv.diem_tb
d["xep_loai"] = sv.xep_loai
data.append(d)
xuat_csv(data, "data/bao_cao_sv.csv")
print(" Đã xuất file data/bao_cao_sv.csv")
2.5. Main
# main.py
from services.sv_service import SVService
def main():
svc = SVService()
actions = {
"1": svc.them_sv,
"2": svc.hien_thi_ds,
"3": svc.tim_kiem,
"4": svc.xoa_sv,
"5": svc.thong_ke,
"6": svc.xuat_bao_cao,
}
while True:
print("\n" + "=" * 45)
print(" QUẢN LÝ SINH VIÊN")
print("=" * 45)
print(" 1. Thêm sinh viên")
print(" 2. Hiển thị danh sách")
print(" 3. Tìm kiếm")
print(" 4. Xóa sinh viên")
print(" 5. Thống kê")
print(" 6. Xuất báo cáo CSV")
print(" 0. Thoát")
print("-" * 45)
chon = input(" Chọn: ").strip()
if chon == "0":
print(" Tạm biệt!")
break
action = actions.get(chon)
if action:
try:
action()
except Exception as e:
print(f" Lỗi: {e}")
else:
print(" Lựa chọn không hợp lệ!")
if __name__ == "__main__":
main()
3. Chạy chương trình
# Tạo cấu trúc thư mục
mkdir -p quan_ly_sv/models quan_ly_sv/services quan_ly_sv/utils quan_ly_sv/data
# Tạo __init__.py
touch quan_ly_sv/models/__init__.py
touch quan_ly_sv/services/__init__.py
touch quan_ly_sv/utils/__init__.py
# Copy code → chạy
cd quan_ly_sv
python main.py
4. Mở rộng
| Tính năng | Kiến thức mới |
|---|---|
| GUI (Tkinter) | Thư viện GUI |
| SQLite | SQL, sqlite3 |
| Web app (Flask) | Web framework |
| Biểu đồ | matplotlib |
| Unit test | pytest |
Tổng kết khoá học
Chúc mừng bạn đã hoàn thành 13 bài Python!
| Giai đoạn | Nội dung |
|---|---|
| Nhập môn | Cài đặt, biến, kiểu dữ liệu, điều kiện, vòng lặp |
| Cấu trúc dữ liệu | String, List, Tuple, Set, Dict, Function |
| Nâng cao | File I/O, OOP, Module, Dự án thực tế |
Bước tiếp theo
- Web: Flask / Django
- Data Science: Pandas, NumPy, Matplotlib
- AI/ML: scikit-learn, TensorFlow
- Game: Pygame
:::tip Lời khuyên "Cách tốt nhất để học lập trình là viết code mỗi ngày. Đừng chỉ đọc — hãy thực hành!" :::
Quay lại: Roadmap