Baris yang lolos validasi jujur, tetapi belum berarti rapi. Nilai selesai dan Selesai sama-sama sah, namun GROUP BY status akan memperlakukan keduanya sebagai dua kelompok berbeda. Pada bagian ini kita menormalkan baris yang lolos: kapitalisasi diikuti ke daftar kanonik, spasi dirapikan, dan angka dikembalikan ke tipe int yang sesuai kontrak skema Bagian 2. Modul src/clean.py tetap murni; modul ini tidak pernah menolak baris.

Pembagian Tugas dengan Validasi #

Validasi dan pembersihan dipisah karena keduanya menjawab pertanyaan berbeda:

AspekValidasi (Bagian 3)Pembersihan (bagian ini)
Pertanyaanapakah nilai ini layak dipercaya?bagaimana menuliskannya dengan konsisten?
Tindakantolak baris, catat alasanubah bentuk, jangan ubah makna
Contoh masalahbudi(at)example.com[email protected]
Hasilbaris dibuangbaris menjadi [email protected]

Pemisahan ini membuat kedua modul dapat diuji terpisah dan diganti terpisah. Ketika aturan bisnis berubah, misalnya kategori baru ditambahkan, hanya satu dari dua modul yang perlu disentuh.

Aturan Pembersihan #

Enam aturan pada bersihkan_baris menjawab enam bentuk ketidak konsistenan dari audit:

KolomSebelumSesudahAturan
order_idord-2024-0006ORD-2024-0006huruf besar
pelangganbudi santosoBudi Santosopangkas spasi, kapital per kata
email[email protected][email protected]huruf kecil
kategoriolahragaOlahragadaftar kanonik
jumlah" 2 "2 (int)pangkas lalu int
harga210.000210000 (int)buang pemisah ribuan

Fungsi bersihkan_baris #

Berikut isi fungsi tersebut di src/clean.py:

python
def bersihkan_baris(baris: dict) -> dict:
    """Normalkan satu baris menjadi baris bersih dengan tipe data sesuai kolom.

    Hasil: order_id huruf besar, tanggal ISO apa adanya, pelanggan kapital
    per kata, email huruf kecil, kategori/status mengikuti daftar kanonik,
    jumlah dan harga bertipe int.
    """
    order_id = rapikan_teks(baris["order_id"]).upper()
    tanggal = rapikan_teks(baris["tanggal"])
    pelanggan = rapikan_teks(baris["pelanggan"]).title()
    email = rapikan_teks(baris["email"]).lower()
    produk = rapikan_teks(baris["produk"])

    kategori_mentah = rapikan_teks(baris["kategori"])
    kategori = KATEGORI_CANONICAL.get(kategori_mentah.lower(), kategori_mentah)

    status_mentah = rapikan_teks(baris["status"])
    status = STATUS_CANONICAL.get(status_mentah.lower(), status_mentah)

    jumlah = normalisasi_angka(baris["jumlah"])
    harga = normalisasi_angka(baris["harga"])

    return {
        "order_id": order_id,
        "tanggal": tanggal,
        "pelanggan": pelanggan,
        "email": email,
        "produk": produk,
        "kategori": kategori,
        "jumlah": jumlah,
        "harga": harga,
        "status": status,
    }

Mari kita telaah tiga keputusan di dalamnya:

  1. rapikan_teks(nilai): memangkas spasi tepi sekaligus melipat spasi beruntun menjadi satu, sehingga " budi santoso " menjadi "budi santoso" sebelum di-.title().
  2. KATEGORI_CANONICAL.get(...): nilai dicari di daftar kanonik dengan kunci huruf kecil. .get diberi nilai balik berupa teks asal, jadi nilai asing tidak pernah diubah paksa; nilai asing seharusnya sudah ditolak validasi.
  3. normalisasi_angka: mengubah "210.000" menjadi 210000 dengan membuang titik sebelum int(). Fungsi ini aman dipanggil karena validasi sudah menjamin polanya angka polos atau pemisah ribuan.

Daftar Kanonik sebagai Satu Sumber #

Daftar kanonik hidup sebagai dua dictionary di bagian atas modul:

python
KATEGORI_CANONICAL = {
    "elektronik": "Elektronik",
    "fashion": "Fashion",
    "makanan": "Makanan",
    "olahraga": "Olahraga",
    "buku": "Buku",
}

STATUS_CANONICAL = {
    "selesai": "Selesai",
    "diproses": "Diproses",
    "dikirim": "Dikirim",
    "dibatalkan": "Dibatalkan",
}

Menyimpan daftar ini di satu tempat berarti menambah kategori baru cukup menambah satu baris di sini dan satu nilai di KATEGORI pada modul validasi. Kedua modul sengaja tidak saling memanggil agar perubahan aturan tidak menjalar ke mana-mana. Rujukan untuk metode teks yang dipakai: str.title .

Sebelum dan Sesudah pada Data Asli #

Kutipan berikut dijalankan nyata terhadap baris ORD-2024-0006 dari berkas Juni, salah satu baris yang lolos validasi tetapi paling kotor:

python
kotor = juni[5]  # baris ke-7 berkas (lolos validasi, belum bersih)
bersih = clean.bersihkan_baris(kotor)

Output yang Dihasilkan:

text
SEBELUM (baris ORD-2024-0006 mentah):
  order_id : 'ord-2024-0006'
  pelanggan: '  budi santoso  '
  email    : '[email protected]'
  kategori : 'olahraga'
  jumlah   : ' 2 '
  harga    : '210.000'
  status   : 'diproses  '

SESUDAH (hasil bersihkan_baris):
  order_id : 'ORD-2024-0006'
  pelanggan: 'Budi Santoso'
  email    : '[email protected]'
  kategori : 'Olahraga'
  jumlah   : 2
  harga    : 210000
  status   : 'Diproses'

Perhatikan bahwa makna tidak berubah sedikit pun: pesanan Budi tetap dua unit matras seharga Rp210.000. Yang berubah hanya penulisan. Untuk seluruh dua berkas, 197 baris lolos validasi dan seluruhnya menghasilkan 197 baris bersih dengan tipe sudah siap dipakai tabel.

Data kini bersih dan bertipe benar, tetapi belum berarti unik: pesanan yang sama masih bisa muncul dua kali dari dua berkas berbeda. Sebelum itu, kita perlu memberi hasil bersih ini sebuah rumah. Lanjutkan ke Bagian 5: Memuat Data ke PostgreSQL.