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:
| Aspek | Validasi (Bagian 3) | Pembersihan (bagian ini) |
|---|---|---|
| Pertanyaan | apakah nilai ini layak dipercaya? | bagaimana menuliskannya dengan konsisten? |
| Tindakan | tolak baris, catat alasan | ubah bentuk, jangan ubah makna |
| Contoh masalah | budi(at)example.com | [email protected] |
| Hasil | baris dibuang | baris 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:
| Kolom | Sebelum | Sesudah | Aturan |
|---|---|---|---|
order_id | ord-2024-0006 | ORD-2024-0006 | huruf besar |
pelanggan | budi santoso | Budi Santoso | pangkas spasi, kapital per kata |
email | [email protected] | [email protected] | huruf kecil |
kategori | olahraga | Olahraga | daftar kanonik |
jumlah | " 2 " | 2 (int) | pangkas lalu int |
harga | 210.000 | 210000 (int) | buang pemisah ribuan |
Fungsi bersihkan_baris #
Berikut isi fungsi tersebut di src/clean.py:
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:
rapikan_teks(nilai): memangkas spasi tepi sekaligus melipat spasi beruntun menjadi satu, sehingga" budi santoso "menjadi"budi santoso"sebelum di-.title().KATEGORI_CANONICAL.get(...): nilai dicari di daftar kanonik dengan kunci huruf kecil..getdiberi nilai balik berupa teks asal, jadi nilai asing tidak pernah diubah paksa; nilai asing seharusnya sudah ditolak validasi.normalisasi_angka: mengubah"210.000"menjadi210000dengan membuang titik sebelumint(). 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:
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:
kotor = juni[5] # baris ke-7 berkas (lolos validasi, belum bersih)
bersih = clean.bersihkan_baris(kotor)Output yang Dihasilkan:
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.