Giai đoạn 2 · Xây tường · Tháng 2

Biến danh sách khách hàng thành tiến độ nhìn thấy được.

Trong 8 buổi B9–B16, bạn đi từ một file CSV lộn xộn đến bộ lọc marketing và hệ thống SQLite theo dõi công trình: Khảo sát → Thi công → Bàn giao.

8 buổi2 buổi/tuần1 giờ/buổiCode dưới 15 dòng

Mục tiêu giai đoạn

Từ “tôi có một danh sách” đến “tôi biết phải gọi ai”.

Sản phẩm tháng 2

Bạn không học cơ sở dữ liệu như một khái niệm trừu tượng. Bạn học cách trả lời những câu hỏi xảy ra mỗi ngày trong remodeling:

  • Khách hàng nào ở đúng Zip Code tại Mỹ để đưa vào Bộ Lọc Marketing?
  • Dự án nào đang ở Khảo sát, dự án nào đã chuyển sang Thi công?
  • Làm sao thêm, đọc, sửa và xóa một hồ sơ mà không làm rối cả danh sách?
50khách hàng tiềm năng mẫu cho telesale
2bảng chính: KhachHang và DuAn
4thao tác CRUD: Thêm, Đọc, Sửa, Xóa
Bảng dữ liệu khách hàng remodeling dạng spreadsheet với tên mẫu, Zip Code, loại dự án và trạng thái liên hệ
Dữ liệu minh họa không chứa người thật: đủ thật để luyện thao tác, đủ an toàn để dùng trong lớp.

Nguyên tắc làm việc

Đừng sửa dữ liệu bằng mắt nếu có thể ghi lại thao tác.

Mỗi bộ lọc, câu lệnh và thay đổi trạng thái đều phải có thể lặp lại. Đó là khác biệt giữa một bảng tính tạm thời và một công cụ vận hành.

Lịch học · B9–B16

Tám buổi, hai lớp năng lực.

Bốn buổi đầu giúp bạn nhìn và hỏi dữ liệu. Bốn buổi sau giúp bạn lưu dữ liệu để hệ thống có thể dùng lại.

Tuần 5–6 · Sổ Sách Kế Toán

Pandas, Excel/CSV và Bộ Lọc Marketing

B9–B12
  1. B9
    Pandas cơ bản: Đọc file Excel & CSV khách hàng Đọc file dữ liệu công trình vào bộ nhớ; hiểu cấu trúc DataFrame gồm hàng (khách hàng) và cột (thuộc tính) nhanh hơn mở bằng Excel thủ công.
    📋 Giáo án Giảng viên & Thực hành B9 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: File Excel 5,000 dòng mở đơ máy ⏱️ 25' Live Code: import pandas as pd, pd.read_csv, head() ⏱️ 20' Thực hành: Đọc file danh sách khách hàng remodeling ⏱️ 5' Nghiệm thu & Tổng kết

    Mục tiêu: Học viên cài đặt thư viện (pip install pandas), đọc thành công file khach_hang.csv vào DataFrame và hiển thị 5 bản ghi đầu tiên bằng df.head().

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Lỗi FileNotFoundError: Học viên để file python và file csv ở 2 thư mục khác nhau. Hướng dẫn mở đúng thư mục làm việc trong VS Code hoặc dùng đường dẫn tương đối.
    • Lỗi mã hóa tiếng Việt (UnicodeDecodeError): Dạy tham số encoding="utf-8" hoặc encoding="utf-8-sig" khi xuất từ Excel sang CSV.

    🛠️ Bài tập thực hành tại lớp

    Bài 1 (Cơ bản): Dùng Pandas đọc file dữ liệu khach_hang.csv. In ra: 5 dòng đầu tiên (df.head(5)), 3 dòng cuối cùng (df.tail(3)) và tổng số dòng/cột có trong bảng (df.shape).
    Bài 2 (Thực chiến nâng cao): Chỉ định hiển thị riêng 2 cột: ["Tên khách hàng", "Số điện thoại"] từ bảng dữ liệu và in ra màn hình.
    Python 3 · Source Code mẫu chuẩn B9b9_pandas_doc_csv.py
    # BUỔI 9: ĐỌC DỮ LIỆU KHÁCH HÀNG BẰNG PANDAS
    import pandas as pd
    
    # Đường dẫn file dữ liệu mẫu công trường
    file_path = "khach_hang.csv"
    
    try:
        # 1. Đọc file CSV vào biến df (DataFrame)
        df = pd.read_csv(file_path, encoding="utf-8")
        
        print("✅ ĐỌC DỮ LIỆU THÀNH CÔNG!")
        so_hang, so_cot = df.shape
        print(f"Tổng số hồ sơ khách hàng: {so_hang} dòng")
        print(f"Tổng số trường thông tin: {so_cot} cột\n")
        
        # 2. Xem nhanh 5 dòng đầu tiên
        print("--- 5 KHÁCH HÀNG MẪU ĐẦU TIÊN ---")
        print(df.head(5))
        
    except FileNotFoundError:
        print(f"❌ Không tìm thấy file '{file_path}'. Hãy kiểm tra vị trí thư mục!")
    # Output kiểm thử: ✅ ĐỌC DỮ LIỆU THÀNH CÔNG! Tổng số hồ sơ khách hàng: 50 dòng Tổng số trường thông tin: 5 cột --- 5 KHÁCH HÀNG MẪU ĐẦU TIÊN --- ID Tên khách SĐT Zip Code Trạng thái liên hệ 0 1 Michael Miller 555-0101 77001 Chưa gọi 1 2 Sarah Johnson 555-0102 77002 Đã liên hệ 2 3 David Martinez 555-0103 77001 Chưa gọi 3 4 Emily Brown 555-0104 77005 Hẹn khảo sát 4 5 James Wilson 555-0105 77001 Chưa gọi
  2. B10
    Khám phá & Kiểm tra chất lượng dữ liệu (Data Inspection) Kiểm tra kiểu dữ liệu (dtypes), tên các cột, phát hiện ô rỗng (missing values) để làm sạch dữ liệu trước khi gửi sang tổ đội thi công.
    📋 Giáo án Giảng viên & Thực hành B10 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Số điện thoại bị mất số 0 hoặc thiếu địa chỉ ⏱️ 25' Live Code: df.info(), df.describe(), df.isna().sum() ⏱️ 20' Thực hành: Soát lỗi dữ liệu hồ sơ công trình ⏱️ 5' Nghiệm thu & Giải đáp

    Mục tiêu: Sử dụng df.info(), df.dtypes và df.isnull().sum() để thống kê các cột bị rỗng, biết cách điền giá trị bù bằng .fillna().

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Cột Zip Code bị nhận nhầm thành int: Nếu Zip Code Mỹ có dạng 07001, khi bị nhận là số nguyên sẽ mất số 0 đầu thành 7001. Dạy cách ép kiểu dtype={"Zip Code": str} khi đọc file.

    🛠️ Bài tập thực hành tại lớp

    Bài 1 (Cơ bản): In danh sách tất cả các cột của DataFrame (df.columns.tolist()). Đếm số lượng ô rỗng trên từng cột bằng df.isna().sum().
    Bài 2 (Thực chiến nâng cao): Nếu cột "Ngân sách dự kiến" bị khuyết dữ liệu, thay thế các ô rỗng đó bằng giá trị 0.0 và in lại kiểm tra.
    Python 3 · Source Code mẫu chuẩn B10b10_kiem_tra_dataframe.py
    # BUỔI 10: SOÁT LỖI VÀ LÀM SẠCH BẢNG DỮ LIỆU
    import pandas as pd
    
    # Ép kiểu Zip Code thành chuỗi để giữ nguyên định dạng số 0 đầu
    df = pd.read_csv("khach_hang.csv", dtype={"Zip Code": str, "SĐT": str})
    
    print("=== BÁO CÁO CẤU TRÚC BẢNG DỮ LIỆU ===")
    df.info()
    
    print("\n=== ĐẾM SỐ DÒNG BỊ THIẾU THÔNG TIN (NULL/EMPTY) ===")
    o_rong = df.isna().sum()
    print(o_rong)
    
    # Xử lý làm sạch: Điền giá trị mặc định cho ô bị thiếu
    if "Trạng thái liên hệ" in df.columns:
        df["Trạng thái liên hệ"] = df["Trạng thái liên hệ"].fillna("Chưa liên hệ")
        print("\n✅ Đã chuẩn hóa các dòng thiếu trạng thái về: 'Chưa liên hệ'")
    # Output kiểm tra: === BÁO CÁO CẤU TRÚC BẢNG DỮ LIỆU === Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 ID 50 non-null int64 1 Tên khách 50 non-null object 2 SĐT 48 non-null object 3 Zip Code 50 non-null object 4 Trạng thái liên hệ 46 non-null object === ĐẾM SỐ DÒNG BỊ THIẾU THÔNG TIN (NULL/EMPTY) === ID 0 Tên khách 0 SĐT 2 Zip Code 0 Trạng thái liên hệ 4
  3. B11
    Lọc dữ liệu theo Zip Code & Sắp xếp ngân sách Lọc khách hàng theo vị trí khu vực thi công tại Mỹ (Zip Code) và sắp xếp dự án theo giá trị hợp đồng từ lớn đến bé để ưu tiên bố trí thợ.
    📋 Giáo án Giảng viên & Thực hành B11 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Thợ chỉ nhận công trình trong bán kính 10 dặm ⏱️ 25' Live Code: df[df["Cot"] == "GiaTri"], sort_values() ⏱️ 20' Thực hành: Lọc danh sách khách hàng khu vực 77001 ⏱️ 5' Nghiệm thu & Thảo luận

    Mục tiêu: Nắm vững kỹ thuật Boolean Indexing lọc dữ liệu trong Pandas và hàm sort_values(by=..., ascending=False).

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Quên gán lại biến: df.sort_values(...) mặc định không thay đổi bảng gốc trừ khi gán lại df = df.sort_values(...) hoặc dùng inplace=True.
    • So sánh kiểu dữ liệu: Nếu cột Zip Code là chuỗi mà so sánh với số 77001 thì kết quả trả về bảng rỗng! Dạy học viên luôn chú ý dấu nháy kép "77001".

    🛠️ Bài tập thực hành tại lớp

    Bài 1 (Cơ bản): Lọc tất cả các khách hàng có Zip Code == "77001". In ra tổng số khách hàng tìm được.
    Bài 2 (Thực chiến nâng cao): Lọc khách hàng có diện tích công trình DienTich > 1000 sqft và sắp xếp danh sách theo thứ tự diện tích từ lớn nhất xuống nhỏ nhất.
    Python 3 · Source Code mẫu chuẩn B11b11_loc_zipcode.py
    # BUỔI 11: LỌC DỮ LIỆU THEO KHU VỰC VÀ SẮP XẾP
    import pandas as pd
    
    df = pd.read_csv("khach_hang.csv", dtype={"Zip Code": str})
    
    # 1. Lọc khách hàng tại khu vực mục tiêu (vd: 77001 - Houston, Texas)
    zip_muc_tieu = "77001"
    danh_sach_khu_vuc = df[df["Zip Code"] == zip_muc_tieu]
    
    print(f"=== DANH SÁCH KHÁCH HÀNG TẠI ZIP CODE {zip_muc_tieu} ===")
    print(f"Tìm thấy: {len(danh_sach_khu_vuc)} khách hàng tiềm năng.")
    
    # 2. Sắp xếp danh sách theo tên khách hàng chữ cái A-Z
    danh_sach_sap_xep = danh_sach_khu_vuc.sort_values(by="Tên khách", ascending=True)
    
    # 3. In kết quả chọn lọc
    print(danh_sach_sap_xep[["ID", "Tên khách", "SĐT", "Zip Code", "Trạng thái liên hệ"]])
    # Output lọc thành công: === DANH SÁCH KHÁCH HÀNG TẠI ZIP CODE 77001 === Tìm thấy: 14 khách hàng tiềm năng. ID Tên khách SĐT Zip Code Trạng thái liên hệ 2 3 David Martinez 555-0103 77001 Chưa gọi 4 5 James Wilson 555-0105 77001 Chưa gọi 0 1 Michael Miller 555-0101 77001 Chưa gọi 12 13 Robert Garcia 555-0113 77001 Chưa gọi
  4. B12
    Hoàn thiện Bộ Lọc Marketing cho Telesale Tự động hóa luồng lọc đa điều kiện: Đúng Zip Code mục tiêu + Trạng thái Chưa gọi + SĐT hợp lệ. Xuất file leads_telesale.csv bàn giao cho bộ phận kinh doanh.
    📋 Giáo án Giảng viên & Thực hành B12 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Bàn giao danh sách telesale sạch cho kinh doanh ⏱️ 25' Live Code: Toán tử & và | trong Pandas, to_csv() ⏱️ 20' Thực hành: Đóng gói script xuất 50 leads ⏱️ 5' Nghiệm thu mốc Tuần 6

    Mục tiêu: Kết hợp nhiều điều kiện lọc bằng &, loại bỏ dòng thiếu SĐT bằng .notna(), giới hạn 50 dòng bằng .head(50) và xuất file bằng to_csv(..., index=False).

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Thiếu dấu ngoặc đơn: Trong Pandas, df[(df["A"] == 1) & (df["B"] == 2)] bắt buộc phải có dấu ngoặc tròn bao quanh từng vế so sánh, nếu không sẽ lỗi TypeError.
    • Xuất file bị cột thừa: Dạy luôn tham số index=False khi gọi .to_csv() để không sinh cột số thứ tự vô nghĩa.

    🛠️ Đồ án mini B12: Bộ Lọc Marketing

    Yêu cầu: Viết chương trình đọc danh sách khách hàng, lọc ra tối đa 50 khách hàng thỏa mãn: Đúng Zip Code 77001, Trạng thái là "Chưa gọi", và Số điện thoại không bị rỗng. Xuất file kết quả ra danh_sach_telesale_hom_nay.csv.
    Python 3 · Source Code hoàn chỉnh B12b12_bo_loc_marketing.py
    # BUỔI 12: BỘ LỌC MARKETING TELESALE CÔNG TRƯỜNG
    import pandas as pd
    
    df = pd.read_csv("khach_hang.csv", dtype={"Zip Code": str, "SĐT": str})
    
    # Lọc đa điều kiện với dấu ngoặc tròn và toán tử &
    dieu_kien = (
        (df["Zip Code"] == "77001") & 
        (df["Trạng thái liên hệ"] == "Chưa gọi") & 
        (df["SĐT"].notna())
    )
    
    leads_chuan = df[dieu_kien].head(50)
    
    print(f"=== BỘ LỌC MARKETING: TÌM THẤY {len(leads_chuan)} KHÁCH HÀNG ĐẠT CHUẨN ===")
    print(leads_chuan[["Tên khách", "SĐT", "Zip Code"]])
    
    # Xuất file kết quả bàn giao cho nhân viên telesale
    ten_file_xuat = "leads_telesale_hom_nay.csv"
    leads_chuan.to_csv(ten_file_xuat, index=False, encoding="utf-8-sig")
    print(f"\n✅ Đã xuất danh sách sẵn sàng gọi điện ra file: '{ten_file_xuat}'")
    # Output xuất file thành công: === BỘ LỌC MARKETING: TÌM THẤY 12 KHÁCH HÀNG ĐẠT CHUẨN === Tên khách SĐT Zip Code 0 Michael Miller 555-0101 77001 2 David Martinez 555-0103 77001 4 James Wilson 555-0105 77001 ... ✅ Đã xuất danh sách sẵn sàng gọi điện ra file: 'leads_telesale_hom_nay.csv'
Sản phẩm hoàn thành: Bộ Lọc Marketing lọc 50 leads theo Zip Code cho telesale.

Tuần 7–8 · Kho Lưu Trữ Dữ Liệu

SQLite, KhachHang, DuAn và CRUD

B13–B16
  1. B13
    Khởi tạo SQLite & Bảng KhachHang Thiết lập kho dữ liệu SQLite bền vững: Tạo database cong_trinh.db và bảng KhachHang với khóa chính tự tăng (Primary Key), lưu trữ an toàn khi tắt app.
    📋 Giáo án Giảng viên & Thực hành B13 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Tại sao dùng SQLite thay vì Excel? ⏱️ 25' Live Code: import sqlite3, CREATE TABLE IF NOT EXISTS ⏱️ 20' Thực hành: Khởi tạo database công trình ⏱️ 5' Nghiệm thu & Giải đáp

    Mục tiêu: Hiểu cơ sở dữ liệu quan hệ, kết nối sqlite3.connect(), tạo bảng có trường khóa chính id INTEGER PRIMARY KEY AUTOINCREMENT, thực hiện commit() và đóng kết nối.

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Quên lệnh db.commit(): Nếu không commit, các câu lệnh tạo bảng hay thêm dữ liệu chỉ nằm trong bộ nhớ đệm RAM và biến mất khi đóng file! Giảng viên dặn: "Commit là động tác cất tài liệu vào két sắt có khóa".

    🛠️ Bài tập thực hành tại lớp

    Bài 1 (Cơ bản): Viết file Python tạo cơ sở dữ liệu cong_trinh.db. Khởi tạo bảng KhachHang với các cột: id (INTEGER PRIMARY KEY), ho_ten (TEXT), sdt (TEXT), zip_code (TEXT).
    Bài 2 (Thực chiến nâng cao): Thêm cột ngay_tao kiểu TEXT lưu ngày giờ tạo hồ sơ (dùng giá trị mặc định CURRENT_TIMESTAMP trong SQLite).
    Python 3 · Source Code mẫu chuẩn B13b13_khoi_tao_sqlite.py
    # BUỔI 13: KHỞI TẠO KHO LƯU TRỮ SQLITE CHO CÔNG TRÌNH
    import sqlite3
    
    # 1. Kết nối hoặc tự động tạo mới file database cong_trinh.db
    conn = sqlite3.connect("cong_trinh.db")
    cursor = conn.cursor()
    
    # 2. Tạo bảng KhachHang nếu chưa tồn tại
    sql_create_table = """
    CREATE TABLE IF NOT EXISTS KhachHang (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        ho_ten TEXT NOT NULL,
        sdt TEXT,
        zip_code TEXT,
        ngay_tao TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    """
    cursor.execute(sql_create_table)
    conn.commit()
    
    print("✅ ĐÃ KHỞI TẠO THÀNH CÔNG BẢNG 'KhachHang' TRONG cong_trinh.db!")
    conn.close()
    # Output kiểm tra: ✅ ĐÃ KHỞI TẠO THÀNH CÔNG BẢNG 'KhachHang' TRONG cong_trinh.db! File cong_trinh.db đã sẵn sàng trong thư mục dự án.
  2. B14
    Thiết kế bảng DuAn & Khóa ngoại liên kết Một chủ nhà có thể có nhiều hạng mục cải tạo. Thiết kế bảng DuAn liên kết với KhachHang bằng trường khóa ngoại khach_hang_id (Foreign Key).
    📋 Giáo án Giảng viên & Thực hành B14 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Khách quen làm thêm tầng 2 & phòng tắm ⏱️ 25' Live Code: Khóa ngoại FOREIGN KEY (khach_hang_id) ⏱️ 20' Thực hành: Tạo bảng quản lý dự án ⏱️ 5' Nghiệm thu & Giải đáp

    Mục tiêu: Hiểu mối quan hệ 1-Nhiều (1 chủ nhà có N công trình), viết câu lệnh DDL tạo bảng DuAn chuẩn kỹ thuật dữ liệu.

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Trùng lặp dữ liệu: Giải thích nếu không có 2 bảng, mỗi lần khách làm thêm dự án mới ta lại phải gõ lại Tên và SĐT của họ trong bảng Dự án → gây sai sót. Có khóa ngoại giúp thông tin khách hàng luôn nhất quán.

    🛠️ Bài tập thực hành tại lớp

    Bài 1 (Cơ bản): Viết câu lệnh SQL tạo bảng DuAn gồm: id, ten_du_an, khach_hang_id, dien_tich_sqft, gia_tri_usd, trang_thai (mặc định: 'Khảo sát').
    Bài 2 (Thực chiến nâng cao): Thiết lập ràng buộc khóa ngoại: FOREIGN KEY (khach_hang_id) REFERENCES KhachHang(id).
    Python 3 · Source Code mẫu chuẩn B14b14_khoi_tao_du_an.py
    # BUỔI 14: THIẾT KẾ BẢNG DỰ ÁN VỚI KHÓA NGOẠI
    import sqlite3
    
    conn = sqlite3.connect("cong_trinh.db")
    cursor = conn.cursor()
    
    sql_create_du_an = """
    CREATE TABLE IF NOT EXISTS DuAn (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        ten_du_an TEXT NOT NULL,
        khach_hang_id INTEGER NOT NULL,
        dien_tich_sqft REAL,
        gia_tri_usd REAL,
        trang_thai TEXT DEFAULT 'Khảo sát',
        FOREIGN KEY (khach_hang_id) REFERENCES KhachHang (id)
    );
    """
    cursor.execute(sql_create_du_an)
    conn.commit()
    
    print("✅ ĐÃ KHỞI TẠO THÀNH CÔNG BẢNG 'DuAn' LIÊN KẾT VỚI 'KhachHang'!")
    conn.close()
    # Output: ✅ ĐÃ KHỞI TẠO THÀNH CÔNG BẢNG 'DuAn' LIÊN KẾT VỚI 'KhachHang'! Cơ sở dữ liệu sẵn sàng cho các thao tác CRUD.
  3. B15
    CRUD: Thêm mới (Create) & Truy vấn (Read) Thực hiện thêm hồ sơ khách hàng mới bằng lệnh INSERT INTO tham số hóa an toàn và truy vấn danh sách bằng SELECT, lọc dự án theo trạng thái.
    📋 Giáo án Giảng viên & Thực hành B15 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Nhập hợp đồng mới ký vào hệ thống ⏱️ 25' Live Code: INSERT INTO với dấu chấm hỏi ?, SELECT * FROM ⏱️ 20' Thực hành: Thêm và đọc hồ sơ công trình ⏱️ 5' Nghiệm thu & Giải đáp

    Mục tiêu: Viết hàm thêm khách hàng nhận dữ liệu từ người dùng, thực thi câu lệnh SQL với tuple tham số an toàn và duyệt các dòng trả về từ cursor.fetchall().

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Lỗ hổng SQL Injection: Nghiêm cấm dùng f-string ghép chuỗi SQL như: f"INSERT INTO KhachHang VALUES ('{ten}')" vì nguy cơ bảo mật. Dạy học viên luôn dùng dấu ? (parameterized query).

    🛠️ Bài tập thực hành tại lớp

    Bài 1 (Cơ bản): Thêm 2 khách hàng mới vào bảng KhachHang. Sau đó viết câu lệnh SELECT * FROM KhachHang và in danh sách ra màn hình.
    Bài 2 (Thực chiến nâng cao): Thêm 1 dự án tương ứng cho khách hàng vừa tạo (lát sàn 650 sqft, trị giá 3,250 USD, trạng thái 'Thi công').
    Python 3 · Source Code mẫu chuẩn B15b15_crud_create_read.py
    # BUỔI 15: THÊM MỚI VÀ TRUY VẤN HỒ SƠ CÔNG TRÌNH
    import sqlite3
    
    def them_khach_hang(ho_ten, sdt, zip_code):
        """Hàm thêm khách hàng mới và trả về ID vừa sinh."""
        with sqlite3.connect("cong_trinh.db") as conn:
            cursor = conn.cursor()
            sql = "INSERT INTO KhachHang (ho_ten, sdt, zip_code) VALUES (?, ?, ?)"
            cursor.execute(sql, (ho_ten, sdt, zip_code))
            conn.commit()
            return cursor.lastrowid
    
    def xem_danh_sach_khach():
        """Hàm truy vấn và in toàn bộ danh sách khách hàng."""
        with sqlite3.connect("cong_trinh.db") as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, ho_ten, sdt, zip_code, ngay_tao FROM KhachHang")
            danh_sach = cursor.fetchall()
            print(f"\n{'ID':<4} | {'HỌ VÀ TÊN':<20} | {'SĐT':<12} | {'ZIP':<6}")
            print("-" * 50)
            for k in danh_sach:
                print(f"{k[0]:<4} | {k[1]:<20} | {k[2]:<12} | {k[3]:<6}")
    
    # Thử nghiệm thêm khách hàng mới
    id_moi = them_khach_hang("Anh Vũ - Căn hộ Vinhomes", "0988776655", "77001")
    print(f"✅ Đã thêm khách hàng mới với mã ID: {id_moi}")
    xem_danh_sach_khach()
    # Output kiểm tra: ✅ Đã thêm khách hàng mới với mã ID: 1 ID | HỌ VÀ TÊN | SĐT | ZIP -------------------------------------------------- 1 | Anh Vũ - Căn hộ Vinhomes | 0988776655 | 77001
  4. B16
    CRUD: Cập nhật tiến độ (Update) & Xóa an toàn (Delete) Cập nhật tiến độ dự án qua 3 mốc: Khảo sát → Thi công → Bàn giao. Viết lệnh UPDATE có điều kiện WHERE chặt chẽ và thao tác DELETE an toàn.
    📋 Giáo án Giảng viên & Thực hành B16 (60 phút)Instructor Kit

    🎯 Mục tiêu bài dạy & Kế hoạch 60 phút

    ⏱️ 10' Tình huống: Bàn giao công trình và thanh lý hợp đồng ⏱️ 25' Live Code: UPDATE ... SET ... WHERE, DELETE an toàn ⏱️ 20' Hoàn thiện Hệ Thống Tiến Độ CRUD ⏱️ 5' Nghiệm thu kết thúc Giai đoạn 2

    Mục tiêu: Học viên làm chủ toàn bộ 4 thao tác CRUD (Create, Read, Update, Delete) với SQLite, hoàn thiện hệ thống quản lý tiến độ công trình chuẩn bị tích hợp lên giao diện Web ở Tháng 3.

    💡 Bẫy lỗi thường gặp & Mẹo sư phạm

    • Thảm họa quên mệnh đề WHERE: Nhấn mạnh lệnh UPDATE DuAn SET trang_thai = 'Bàn giao' mà quên WHERE id = ? sẽ biến TOÀN BỘ công trình trong công ty thành đã bàn giao! Giảng viên dặn: "Luôn kiểm tra điều kiện WHERE trước khi gõ phím Enter".

    🛠️ Đồ án tốt nghiệp Giai đoạn 2 tại lớp

    Yêu cầu: Viết chương trình menu dòng lệnh tương tác cho phép giám sát: (1) Xem danh sách dự án, (2) Chuyển trạng thái dự án (ví dụ từ 'Khảo sát' sang 'Thi công' hoặc 'Bàn giao'), (3) Xóa một dự án bị hủy thầu.
    Python 3 · Source Code hoàn chỉnh B16b16_he_thong_tien_do.py
    # BUỔI 16: HỆ THỐNG CẬP NHẬT TIẾN ĐỘ THI CÔNG HOÀN CHỈNH
    import sqlite3
    
    def cap_nhat_tien_do(du_an_id, trang_thai_moi):
        """Cập nhật tiến độ công trình: Khảo sát -> Thi công -> Bàn giao."""
        trang_thai_hop_le = ["Khảo sát", "Thi công", "Bàn giao", "Đã thanh lý"]
        if trang_thai_moi not in trang_thai_hop_le:
            print(f"❌ Trạng thái '{trang_thai_moi}' không hợp lệ!")
            return False
            
        with sqlite3.connect("cong_trinh.db") as conn:
            cursor = conn.cursor()
            sql = "UPDATE DuAn SET trang_thai = ? WHERE id = ?"
            cursor.execute(sql, (trang_thai_moi, du_an_id))
            conn.commit()
            if cursor.rowcount > 0:
                print(f"✅ ĐÃ CẬP NHẬT DỰ ÁN #{du_an_id} SANG TRẠNG THÁI: '{trang_thai_moi}'")
                return True
            else:
                print(f"⚠️ Không tìm thấy dự án mang mã ID #{du_an_id}!")
                return False
    
    def xoa_du_an(du_an_id):
        """Xóa an toàn một bản ghi dự án với điều kiện ID."""
        with sqlite3.connect("cong_trinh.db") as conn:
            cursor = conn.cursor()
            cursor.execute("DELETE FROM DuAn WHERE id = ?", (du_an_id,))
            conn.commit()
            print(f"🗑️ Đã xóa dự án #{du_an_id} khỏi hệ thống.")
    
    # Thử nghiệm đổi trạng thái dự án sang Bàn giao
    cap_nhat_tien_do(1, "Thi công")
    cap_nhat_tien_do(1, "Bàn giao")
    # Output kiểm thử tiến độ: ✅ ĐÃ CẬP NHẬT DỰ ÁN #1 SANG TRẠNG THÁI: 'Thi công' ✅ ĐÃ CẬP NHẬT DỰ ÁN #1 SANG TRẠNG THÁI: 'Bàn giao' Hệ thống sẵn sàng bàn giao cho giai đoạn Web Streamlit!
Sản phẩm hoàn thành: Hệ Thống Tiến Độ SQLite: Khảo sát → Thi công → Bàn giao.

Worked examples

Nhìn thấy từng bước trước khi tự gõ.

Một quy trình tốt luôn bắt đầu bằng câu hỏi: dữ liệu đang ở đâu, và thao tác này có làm mất dữ liệu gốc không?

01 Đọc và lọc danh sách

  1. 1Đọc file bằng pd.read_csv("khach_hang.csv").
  2. 2Kiểm tra bằng df.head() và df.dtypes.
  3. 3Giữ các dòng có Zip Code đúng điều kiện.
  4. 4Sắp xếp theo tiêu chí dự án để ưu tiên cuộc gọi.

02 Đưa hồ sơ vào kho

  1. 1Tạo bảng KhachHang và DuAn.
  2. 2Thêm một bản ghi bằng INSERT.
  3. 3Đọc lại bằng SELECT để xác nhận.
  4. 4Dùng UPDATE đổi trạng thái; DELETE chỉ khi chắc chắn.
Minh họa kho lưu trữ dữ liệu với hai ngăn KhachHang và DuAn, cùng các thao tác Thêm, Đọc, Sửa, Xóa
Ẩn dụ “kho lưu trữ”: KhachHang giữ người, DuAn giữ công trình; CRUD là quy trình quản lý hồ sơ trong kho.

Hệ thống tiến độ

Một trạng thái rõ ràng giúp cả đội nói cùng một ngôn ngữ.

Sau B16, hồ sơ công trình không còn nằm rải rác trong tin nhắn. Trạng thái được cập nhật trong hệ thống và có thể đọc lại khi cần.

Thao tácVí dụ tại công trườngCâu lệnh tư duy
CreateThêm dự án mớiHồ sơ này chưa có trong kho
ReadĐọc danh sách dự ánHiện có những gì?
UpdateKhảo sát → Thi côngThông tin đã thay đổi
DeleteXóa bản ghi mẫuHồ sơ này không còn cần giữ
Bảng tiến độ công trình dạng kanban với ba cột Khảo sát, Thi công và Bàn giao
Kanban là cách nhìn trực quan; SQLite là nơi lưu trạng thái để lần sau mở lại vẫn còn.
Mốc Nghiệm Thu Giai Đoạn 2 Hoàn Tất

Sẵn Sàng Đưa Dữ Liệu Lên Giao Diện Web?

Bạn đã làm chủ lọc khách hàng theo Zip Code bằng Pandas và quản trị tiến độ thi công CRUD bằng SQLite. Ở Giai đoạn 3, toàn bộ dữ liệu này sẽ được kết nối trực tiếp với giao diện Web Streamlit dùng trên điện thoại.

Sang Giai đoạn 3: Streamlit Web App →
Zalo 📞