2010
Daftar Isi
. . . . . . . . . . . . . . . . . . . . .
4 5 8
9 11 13
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Tipe Data
4.1 4.2 String 4.2.1 4.2.2 4.2.3 4.3 List 4.3.1 4.3.2 4.3.3 4.4 4.5
16
16 17 18 18 19 19 20 21 24 25 27
Bilangan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Mengubah Elemen . . . . . . . . . . . . . . . . . . . . . .
Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Waktu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5 Modularitas
5.1 5.2 5.3
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
32
31
Search Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
6 Fungsi
6.1 6.2 7.1
. . . . . . . . . . . . . . . . . . . . .
36
36 36
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7 Database
7.1.1 7.1.2
Tabel
41
43 45 48
DAFTAR ISI
View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Backup dan Restore 7.3.1 7.4.1 7.4.2 Encoding PL/pgSQL PL/Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
50 52 53 54 55 60
Fungsi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
70 73 75
63
72
77
9 Lintas Sistem
9.1 9.2 9.3 9.4 File Database 9.4.1 9.4.2
Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
79
79 81 82 83 86 87
10 Pengemasan
89
89 94 95
98
99
105
14.1 Pemasangan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 14.1.1 IMEI Chip 14.1.2 Database . . . . . . . . . . . . . . . . . . . . . . . . . . 116 . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 . . . . . . . . . . . . . . . . . . . . . 118
111 114
14.2 Hello world! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 14.3 Instant Messenger Gateway 14.3.1 Yahoo! Messenger . . . . . . . . . . . . . . . . . . . . . . 118
DAFTAR ISI
Bab 1
Alasan
Mengapa Python merupakan bahasa yang tepat untuk pembuatan berbagai aplikasi ? Berikut ini alasannya.
bagai platform seperti Linux, Windows, Mac, Unix. Bahkan sudah tersedia di platform handphone seperti Symbian dan Android. Python tergolong scripting, artinya Anda cukup tulis source-nya di
text editor biasa, lalu jalankan. Bahasa ini berkonsep hemat source. Tampak pada cara penulisannya
yang tidak membutuhkan karakter atau kata khusus untuk BEGIN dan END. Sebagai gantinya sub-block dipisahkan dengan indent (penulisan menjorok ke kanan).
Multiplatform Lengkap
ki sifat umum traditional database seperti transaction, stored procedure (function), dan trigger. Bahkan function bisa ditulis dalam berbagai bahasa (tidak hanya SQL), Python, Perl, Shell, dan Java adalah contoh bahasa yang didukungnya. Ia juga memiliki modul khusus untuk kebutuhan GIS (Geographic Information System).
Bab 2
Pemasangan
Untuk memudahkan penjelasan, kita gunakan distro Linux berbasis Ubuntu. Pada saat tulisan ini dibuat kami menggunakan Ubuntu 10.04 (Lucid). Anda juga bisa menggunakan jenis Ubuntu lainnya seperti Kubuntu (window manager KDE), atau BlankOn (buatan Indonesia). Pasca instalasi Ubuntu Python sudah disertakan. Namun masih ada yang perlu diunduh (download) lagi. Sebelumnya perbaharui daftar paket yang akan diunduh, lakukan ini di konsole:
sudo
a p t g e t
update
Pasang PostgreSQL:
sudo
a p t g e t
install
postgresql
p o s t g r e s q l p l p y t h o n
8.4
plpython digunakan saat kita membuat function di PostgreSQL, alternatif lain dari plpgsql yang terpasang secara default. Pasang SQLAlchemy agar Python bisa menggunakan PostgreSQL:
sudo
a p t g e t
install
python s q l a l c h e m y
python p s y c o p g 2
Saat pemasangan paket postgresql secara otomatis paket postgresql-client-8.4 terpasang. web-nya: Paket ini memuat psql yang digunakan untuk terhubung ke PostgreSQL server, secara command line. Ada baiknya Anda juga memasang versi
sudo
a p t g e t
install
phppgadmin
sudo
/ e t c / i n i t . d/ apache2
start
Secara default Apache hidup otomatis saat komputer boot. Lalu buatlah symlink phppgadmin:
sudo
ln
/ u s r / s h a r e / phppgadmin
/ v a r /www/
BAB 2.
PEMASANGAN
http://localhost/phppgadmin
Bagi pengguna window manager Gnome Anda bisa gunakan gedit sebagai text editor. Sedangkan pengguna KDE bisa gunakan kate. Keduanya berbasis GUI. Jika Anda lebih nyaman dengan konsole bisa gunakan nano, joe, atau vi.
sudo
a p t g e t
install
vim
sudo
vi
/ e t c / vim / v i m r c
Biasanya option sudah disediakan, Anda tinggal menghapus tanda kutip di awal option. Aktifkan pewarnaan agar nyaman saat membaca source:
1 2 3
later
support
syntax by
highlighting .
syntax
highlighting
default .
Saat Anda membuka le untuk keduakalinya, maka kursor langsung menuju ke lokasi sebelumnya.
1 2 3 4 5
the a
following
to
h a v e Vim jump
to
the
last
when file
if
l i n e ( " ' \ " " ) > 0 && l i n e ( " ' \ " " ) <= " normal g '\"" | endif
exe
Menjorok otomatis (auto indent), dan penekanan tombol TAB diganti dengan SPACE 4 kali:
1 2 3 4 5 6 7 8 9 10 11
the
to
h a v e Vim
load
according
filetype . specific
default
D e b i a n Vim
h a s ( " autocmd " ) filetype set set set set set indent on smartindent expandtab t a b s t o p =4 s o f t t a b s t o p =4 s h i f t w i d t h =4
set
ignorecase
" Do
case
insensitive
matching
BAB 2.
PEMASANGAN
Bila kata yang dicari mengandung huruf besar dan huruf kecil, maka pencarian memperhatikan huruf besar atau kecil (casesensitive).
set
smartcase
" Do s m a r t
case
matching
Aktifkan kedua opsi pencarian di atas, maka pencarian di vim ( menggunakan perintah slash / ) semakin mudah.
Bab 3
Hello World!
Anda bisa memulai Python dalam modus interaktif. Nanti kalau source sudah mulai panjang kita simpan dalam le. Modus interaktif dijalankan di konsole, lalu ketik:
1 2 3 4 5
2.6.5
Apr
16
2010 , or
information .
Mulailah dengan yang sederhana, menampilkan sebuah pesan menggunakan perintah print.
1 2
' Hello
World ! '
World !
Gunakan tombol panah atas untuk mengulang perintah sebelumnya, lalu kita coba sedikit salah satu ciri pemgrograman berorientasi objek (object oriented programming / OOP).
1 2 3 4
HELLO W O R L D! world !
'Hello World!' adalah sebuah string, yaitu DATA yang boleh berisi alphanumeric, boleh terdiri dari huruf dan angka atau karakter lainnya. Contoh di atas menyebutkan bahwa string tidak hanya berisi data, ia juga memuat FUNGSI yang bernama upper() dan lower(). fungsi. Dengan demikian 'Hello World!' bukan sekedar string, tapi disebut OBJEK string. Inilah ciri objek, memuat data dan
BAB 3.
HELLO WORLD!
Bingung ?
Untuk keluar
3.1
Selain menggunakan kutip tunggal, string juga dapat dibatasi oleh kutip ganda:
1 2
" Hari
Jum ' a t
Bila string lebih dari satu baris Anda bisa gunakan kutip tiga kali:
1 2 3 4 5 6
Bummi Dwi
Putera
Alamat :
Nama : Hobi :
Alamat :
Perhatikan tiga buah titik di atas yang berarti sebuah perintah belum berakhir. Selanjutnya marilah membuat script yang akan menanyakan nama, alamat, dan hobi, lalu menampilkannya kembali di layar. Kali ini kita simpan dalam sebuah le pegawai.py. kate, nano, joe, atau vi. Listing 3.1: pegawai.py Anda bisa gunakan text editor favorit seperti gedit,
1 2 3 4 5 6
a l a m a t = r a w _ i n p u t ( ' Alamat :
h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' Hobi : ' , ' , nama ' , alamat hobi ' Alamat :
python
p e g a w a i . py
1 2 3
Nama : Hobi :
Putera
Alamat :
raw_input() adalah fungsi untuk menerima masukkan dari user. Fungsi ini menghasilkan string yang berisi masukkan user tersebut, dan dilimpahkan ke variabel nama, alamat, dan hobi. Anda bisa kembali ke modus interaktif untuk sekedar mengetahui tipe data dari variabel nama.
BAB 3.
HELLO WORLD!
10
1 2 3 4
> > > nama = r a w _ i n p u t ( ' Nama : Nama : <t y p e Bummi t y p e ( nama ) ' s t r '> > > > print
')
Pada script di atas kita mulai mengenal apa yang disebut variabel, yaitu nama, alamat, dan hobi. Variabel bisa dikatakan sebagai penampung. Tipe data ketiga variabel di atas adalah string. Mari kita coba tipe data lainnya.
1 2 3 4 5 6
> > > a = 3 > > > b = 5 > > > print 8 > > > print <t y p e type ( a ) ' i n t '> Berikut ini untuk a + b
Variabel a dan b bertipe integer alias bilangan bulat. bilangan pecahan (oat).
1 2 3
> > > c = 7.2 > > > print <t y p e type ( c ) ' f l o a t '>
Ya, pemisah bilangan bulat dengan pecahannya adalah dengan titik. Oh ya, Python tergolong ketat dalam hal pengoperasian antar tipe data. diperkenankan menambahkan string dengan integer. Kita tidak
1 2 3 4 5 6
> > > nama = > > > print Traceback File TypeError :
' Bummi ' ' usia line ' + umur + call 1, last ) : ' str ' and ' int ' objects ' tahun '
i n <module>
concatenate
1 2
nama + 24
'
usia
' +
s t r ( umur ) +
'
tahun '
tahun
1 2
'% s 24
u s i a %s tahun
umur )
Meski string tidak bisa ditambah dengan integer, namun string bisa dikalikan dengan integer:
1 2
' ab '
10
abababababababababab Kembali ke le pegawai.py, tidak diperkenankan menulis tidak rapi. Cobalah membuat kesalahan di baris terakhir pada le pegawai.py, yaitu dengan menambahkan dua spasi sebelum print:
BAB 3.
HELLO WORLD!
11
1 2 3 4 5 6
nama = r a w _ i n p u t ( ' Nama : h o b i = r a w _ i n p u t ( ' Hobi : print print ' Nama : ', nama ', ', alamat hobi ' Alamat : ' Hobi :
a l a m a t = r a w _ i n p u t ( ' Alamat :
Kemudian jalankan:
python
p e g a w a i . py
hasilnya Python protes karena ada indent (menjorok masuk) yang tidak diperkenankan:
1 2 3 4 5
" p e g a w a i . py " ,
IndentationError :
unexpected
Seperti kita lihat, script Python tanpa diawali suatu BEGIN .. END atau { .. } atau berbagai penanda lainnya sebagai bentuk awal dan akhir program. Baris-baris utama selalu tanpa indent alias rapat kiri. kondisi (if ). Baris yang memiliki indent berarti dianggap bagian dari sub-blok seperti dalam looping (for) atau
1 2 3 4 5 6 7
nama = r a w _ i n p u t ( ' Nama : h o b i = r a w _ i n p u t ( ' Hobi : print print if ' Nama : ', nama ', ', alamat hobi ' Alamat : ' Hobi :
a l a m a t = r a w _ i n p u t ( ' Alamat :
hobi : print
3.2
Kondisi
Python punya semacam pedoman dalam hal kondisi (if ), dimana jika suatu variabel ada isinya maka True, jika kosong maka False.
1 2
if
1 2
if
hobi print
!=
' Hobi :
Secara tipe data hobi tentulah sebuah string, tapi bagaimana dengan hobi != ? Mari kita uji di modus interaktif dimana variabel hobi ada isinya.
BAB 3.
HELLO WORLD!
12
False 0 0 [] {} None
True (Contoh) 'abc' 1 1.2 [10,20,30] {'nama': 'Bummi', 'alamat': 'Bogor'} True
1 2 3 4 5
> > > hobi = > > > hobi True > > > h o b i == False !=
1 2 3 4 5
> > > hobi = > > > hobi False > > > h o b i == True !=
Pahami baik-baik perbedaan keduanya. Perhatikan juga penggunaan karakter samadengan dua kali ( == ) yang berarti operasi logika (boolean operation).
1 2 3
''
Kembali ke pegawai.py dimana bila hobi tidak diisi maka program akan memberikan saran. Salinlah menjadi pegawai1.py seperti berikut ini. Listing 3.2: pegawai1.py
1 2 3 4 5 6 7 8 9
a l a m a t = r a w _ i n p u t ( ' Alamat :
h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' , nama ' , ' , alamat hobi hobi ' Alamat : ' Hobi :
hobi :
' Sebaiknya
else digunakan untuk kondisi sebaliknya (False). tambahan ? hari Sabtu ditampilkan.
BAB 3.
HELLO WORLD!
13
1 2 3 4 5 6 7 8 9 10 11
a l a m a t = r a w _ i n p u t ( ' Alamat :
h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' , nama ' , alamat ' Alamat :
' Sebaiknya
Perhatikan juga penggunaan titik dua ( : ) pada if dan else. Ini ciri lain untuk menandai awal suatu sub-blok. Jadi bisa dipastikan setelah titik dua baris berikutnya selalu menjorok ke dalam (indent).
3.3
Perulangan
Mari kita buat input pegawai jadi lebih mudah, dimana program akan menanyakan data terus-menerus dan baru berhenti bila nama tidak diisi.
1 2 3 4 5 6 7 8 9 10 11 12
')
a l a m a t = r a w _ i n p u t ( ' Alamat : h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' , nama ' , ' , alamat hobi hobi ' Alamat : ' Hobi : ')
')
hobi :
' Sebaiknya
diisi . '
Perhatikan baris pertama yang berarti perulangan tanpa henti. Yang menghentikannya adalah baris ke empat. break adalah kata khusus untuk menghentikan perulangan dimana alur keluar menuju blok bawah di luar perulangan tersebut. Kebetulan dalam contoh ini tidak ada blok lain di luar perulangan. Selanjutnya kita buat aturan baru untuk pengisian data ini, dimana: 1. Nama dan alamat harus diisi. 2. Bila selesai satu data pegawai maka program akan menanyakan apakah akan memasukkan data berikutnya.
BAB 3.
HELLO WORLD!
14
Di sini akan kita ubah sedikit logika while-nya. Listing 3.5: pegawai4.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
while if not print continue if not print continue print print if print else print
lanjut nama : ' Nama : hobi : :
lanjut =
'Y '
!=
h o b i = r a w _ i n p u t ( ' Hobi : ' , nama ' , ' , alamat hobi hobi ' Alamat : ' Hobi :
' Sebaiknya
l a n j u t = r a w _ i n p u t ( ' Tekan
continue pada baris ke 6 menandakan alur kembali ke while pada baris ke 2. Perhatikan juga penggunaan raw_input() pada baris terakhir. Karena fungsi ini mengembalikan nilai string maka kita bisa lanjutkan dengan memanggil fungsi upper(). Dengan cara ini user diperbolehkan memasukkan dengan s kecil maupun S besar. while cocok untuk perulangan yang kondisinya tidak menentu. Bagaimana bila kita butuh perulangan yang jumlahnya sudah diketahui ? Misalkan cetak angka 1 hingga 5.
1 2
for
in
[1 ,2 ,3 ,4 ,5]: i
print Hasilnya:
1 2 3 4 5
Perhatikan [1,2,3,4,5] yang merupakan data bertipe list atau sering juga disebut sebagai array. Kita bisa persingkat penulisannya dengan fungsi range().
1 2
for
in i
range (5) :
Hasilnya
BAB 3.
HELLO WORLD!
15
0 1 2 3 4
Meski tidak dimulai dari 1 tapi jumlah barisnya tetap 5. Jika tetap ingin dimulai dari 1 gunakan range(1,6).
1 2
for
in i
range ( 1 , 6 ) :
Karena for merupakan perulangan juga sebagaimana while, maka perintah break dan continue juga berlaku di dalamnya.
Bab 4
Tipe Data
Memperhatikan tipe data salah satu pokok pada Python dan ini juga menjadi salah satu kunci agar mudah dalam pencarian kesalahan (debugging).
4.1
String
> > > nama = Agus Jika Anda butuh kutip tunggal di dalam string, siasatilah.
> > > h a r i = Jum ' a t Masih belum yakin apa tipe data variabel nama ? Gunakan type().
1 2
Data bertipe string bisa berisi karakter apa saja seperti huruf, angka, tanda baca, atau gabungannya. Menggabungkan dua buah variabel string bisa menggunakan tanda plus ( + ).
1 2
nama + hari
'
lahir
hari
' + hari
Jum ' a t
Namun cara ini tidak disarankan karena membuat program menjadi sulit dibaca. Kalau program sulit dibaca menyulitkan penelusuran bila ada kesalahan (debugging). Jadi sebaiknya gunakan formatting.
1 2
'% s
lahir
h a r i %s ' % ( nama ,
hari )
hari
Jum ' a t
16
BAB 4.
TIPE DATA
17
Lalu bagaimana menampilkan % itu sendiri di dalam formatting ? Sebutkanlah dua kali.
1 2
bulan
ini
m e n i n g k a t %d %%' % ( 1 0 ) 10 %
meningkat
4.2
Bilangan
Bilangan bulat atau integer atau disingkat int dinyatakan tanpa titik.
1 2
>>> a + 2 12
a dikurang 4
1 2
a dikali 5
1 2
a dibagi dengan 3
1 2
Mengapa bukan 3.3333 ? Pembagian bilangan bulat dengan bilangan bulat menghasilkan bilangan bulat juga. Jika Anda mengharapkan hasil yang lebih rinci maka salah satunya harus bilangan pecahan (oat).
1 2
3.0
1 2
1 2
**
BAB 4.
TIPE DATA
18
4.2.1
1 2
1 2
int (10.2)
1 2 3 4
> > > round ( 1 0 . 4 ) 10 > > > round ( 1 0 . 5 ) 11 round() akan membulatkan ke bawah bila pecahan suatu bilangan lebih kecil dari 0,5. Sebaliknya ia akan membulatkan ke atas. Untuk konversi dari string menjadi bilangan pecahan menggunakan fungsi oat().
1 2
4.2.2
Formatting
1 2 3 4
> > > a = 3 > > > b = 2 > > > print 3 + 2 = 5 Meski formatting %s bisa digunakan untuk tipe data apa saja, sebaiknya Anda menggunakan formatting yang lebih spesik sesuai tipe datanya. Katakanlah Anda menetapkan bahwa variabel a itu harus bertipe bilangan bulat (integer), begitu pula dengan variabel b, maka gunakanlah %d. '% s + %s = %s ' % ( a , b, a+b )
1 2
'%d + %d = %d ' % ( a ,
b,
a+b )
1 2 3
> > > a = 3.5 > > > print '% f + %f = %f ' % ( a , b, a+b ) 3.500000 + 2.000000 = 5.500000 Anda juga bisa mengatur jumlah digit di belakang koma.
1 2
BAB 4.
TIPE DATA
19
4.2.3
Kalkulator
Buatlah le
Membuat kalkulator tidaklah sulit, Anda cukup gunakan eval(). calc.py berikut ini.
1 2 3 4 5
eval ( hitung )
Jalankan.
1 2 3 4 5 6 7 8 9
c a l c . py 2+3 7 ** 2 7 + 2
Perhatikan baik-baik. Semua kalimat Python diterjemahkan oleh eval(), baik kalimat itu mengandung spasi ataupun tidak, eval() sanggup memahaminya. Mudah bukan? Perhatikan juga kalimat 7 + 2 * 3 yang menghasilkan nilai 13. Ini artinya aturan prioritas pada matematika umum berlaku, dimana perkalian lebih dulu dilakukan ( 2 * 3 ). Barulah hasilnya ( 6 ) ditambahkan dengan 7. Program di atas akan terus meminta masukkan dari user hingga tombol Enter saja yang ditekan.
4.3
List
Tipe data list kerap digunakan. Kita lihat pada contoh sebelumnya bagaimana list menjadi wajib pada perulangan for. Bahkan string sebenarnya bisa dianggap sebagai list.
1 2
for
ch
in
ch ,
hasilnya:
Bummi Perhatikan penggunaan koma pada print yang berarti jangan ganti baris. Mari kembali ke modus interaktif untuk mencoba list.
BAB 4.
TIPE DATA
20
1 2 3 4 5
> > > data = > > > print ' Bummi Dwi > > > print ' Bogor '
1 2
data [ 1]
1 2
data [ 2]
4.3.1
Pemenggalan
Selain dapat diambil per elemen, juga dapat diambil beberapa elemen sekaligus, ini disebut sebagai pemenggalan (slicing). Tampilkan elemen pertama dan kedua:
1 2
1 2
1 2
data [
3:]
24]
Ada baiknya kita pahami cara kerjanya. Pemenggalan list bekerja dengan batas elemen. sebagai list. Gambar 4.1 adalah contoh string Python yang bisa dianggap Cermati baik-baik bagaimana bekerja dengan elemen dan batas
BAB 4.
TIPE DATA
21
4.3.2
Keberadaan Elemen
Setelah user
Anda memiliki daftar nama buah yang dapat dicari oleh user.
memasukkan nama buah program akan mencarinya dalam list dan memberitahukan hasilnya. Setelah itu program akan kembali menanyakan nama buah berikutnya. Program berakhir jika user hanya menekan Enter saja, alias tidak memasukkan apapun. Sekarang buatlah produk.py berikut ini. Listing 4.2: produk.py
1 2 3 4 5 6 7 8 9 10 11
daftar =
[ ' j e r u k ' , ' mangga ' , ' a p e l ' , ' p i s a n g ' , ' jambu ' ]
buah :
')
1 2 3 4 5 6 7
p r o d u k . py mangga duku
ditemukan
Selesai Keberadaan elemen bisa digunakan sebagai kondisi. Misalkan program Anda dapat menerima masukan (input parameter) sesaat sebelum dijalankan, sering disebut sebagai argument. Seperti pada contoh cat.py berikut ini.
1 2
import print
$
Jalankanlah.
1 2
python
c a t . py
1 2
python
c a t . py
/ etc / hosts
[ ' c a t . py ' ,
Perhatikan elemen pertama adalah le cat.py itu sendiri, dan elemen kedua adalah nama le yang akan ditampilkan. Ya, kita akan membuatnya dapat membuka le yang disebutkan dan menampilkannya di layar.
BAB 4.
TIPE DATA
22
1 2 3 4 5 6
import print
$
1 2 3 4 5 6 7 8 9 10 11
python
c a t 1 . py
/ etc / hosts localhost compaq lines are desirable for IPv6 capable compaq
127.0.0.1 127.0.1.1 # The ::1 fe00 : : 0 ff00 ::0 ff02 ::1 ff02 ::2 ff02 ::3 following
Kita perlu mengantisipasi kesalahan yang dilakukan user, dimana bisa saja ia tidak tahu cara menggunakan cat1.py, yaitu langsung menjalankan tanpa menyertakan nama le.
1 2 3 4 5
python File
Traceback
Tampilan kesalahan ini jelas kurang informatif dan bisa jadi user tidak tahu apa yang harus dilakukan. Saatnya menggunakan deteksi keberadaan elemen.
1 2 3 4 5 6 7 8 9 10
sys
sys . argv [ 1 : ] : ( ' Cara menggunakannya : p y t h o n %s <nama f i l e > ' % sys . argv [ 0 ] )
BAB 4.
TIPE DATA
23
1 2
python
Cara
menggunakannya :
Kini program akan menampilkan petunjuk cara menggunakannya. Mari kita bahas cara kerjanya. Perhatikan baris ketiga
if not sys.argv[1:]:
Kalau kita perhatikan lagi isi dari sys.argv pada saat tidak diberikan input parameter adalah:
['cat.py']
Dengan begitu sys.argv[1:] akan bernilai list hampa:
[]
Ingatlah mengenai pemenggalan list pada pembahasan sebelumnya. Bila suatu variabel hampa maka ia bisa dianggap sebagai boolean False, sehingga
if not sys.argv[1:]:
bisa berarti
if not []:
yang berarti
if not False:
dan ini bisa diartikan menjadi:
if True:
True berarti kondisi terpenuhi dan blok di dalam if dijalankan, dan akhirnya tampillah pesan cara penggunaan cat2.py tadi. Lalu apa yang terjadi jika program mendapat input parameter ? sys.argv menjadi Nilai
['cat.py', '/etc/hosts']
Dengan begitu
if not sys.argv[1:]:
bisa berarti
if not ['/etc/hosts']:
BAB 4.
TIPE DATA
24
Karena ['/etc/hosts'] adalah list yang berisi (tidak hampa) maka bisa dianggap sebagai boolean True
if not True:
dan ini berarti juga
if False:
False berarti kondisi tidak terpenuhi sehingga blok di dalam if tidak dijalankan. Pahamilah baik-baik penjelasan ini, Anda akan banyak menemuinya nanti. Inilah salah satu mengapa program yang dibuat dengan Python begitu ringkas namun tetap mudah dibaca.
4.3.3
Mengubah Elemen
Contoh sebelumnya menjelaskan bagaimana menggunakan list. Kini kita coba berawal dari list hampa dan mengisinya dengan elemen data. Kembali ke modus interaktif.
1 2 3
[]
Tambahkan mangga.
1 2 3
> > > d a f t a r . append ( ' mangga ' ) > > > daftar [ ' mangga ' ] Bisa juga dengan cara lain.
1 2 3
> > > d a f t a r += [ ' p i s a n g ' ] > > > daftar [ ' mangga ' , ' pisang ' ]
Kita akan ubah elemen kedua (index ke 1) dari pisang menjadi jeruk.
1 2 3
> > > daftar [ 1 ] > > > daftar [ ' mangga ' ,
1 2 3
daftar [ 0 ]
BAB 4.
TIPE DATA
25
4.4
Dictionary
List adalah serangkaian elemen yang alamatnya adalah nomor urut, sering disebut sebagai index. Dictionary juga mirip, hanya saja alamatnya tidak harus berupa angka yang berurutan. Bahkan bisa bertipe string atau tipe data lainnya. Sekarang buatlah script berikut ini. Listing 4.6: produk1.py
1 2 3 4 5 6 7 8 9
daftar = { 2: 7: 4: 3: 9: } ' jeruk ' , ' mangga ' , ' pisang ' , ' jambu ' , ' apel ' ,
print
$ {9:
daftar
Jalankan.
1 2
python
p r o d u k 1 . py 2: ' jeruk ' , 3: ' jambu ' , 4: ' pisang ' , 7: '
Perhatikan urutan pada source, dan bandingkan urutan buah saat ditampilkan. Begitulah dictionary, dia memang tidak memperhatikan urutan. Inilah salah satu yang membedakannya dengan list. Dictionary cocok untuk pencarian. Pada contoh di atas angka 2, 7, 4, 3, dan 9 merupakan kunci (key) bagi variabel daftar. Key ini bisa saja dianggap sebagai kode barang atau kode buah. Sedangkan apel, jeruk, jambu, pisang, dan mangga merupakan nilai (value). Karena itu dictionary sering disebut sebagai tipe data dengan formasi key value. Sekarang kita buat pencarian berdasarkan kode barang. Listing 4.7: produk2.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14
daftar = { 2: 7: 4: 3: 9: } ' jeruk ' , ' mangga ' , ' pisang ' , ' jambu ' , ' apel ' ,
barang :
')
BAB 4.
TIPE DATA
26
15 16 17
else
$ Kode Kode Kode Kode Kode
print print
:
d a f t a r [ key ] ' Kode barang ' , key , ' tidak ditemukan '
Cobalah.
1 2 3 4 5 6 7
python
Selesai Kode barang 5 boleh jadi tidak ditemukan, karena memang tidak ada 5 dalam variabel daftar. Tapi mengapa 9 juga tidak ditemukan ? Seharusnya program menampilkan apel. Jawabannya ada pada tipe data. Fungsi raw_input() itu mengembalikan nilai bertipe string. Berarti key pada baris
if key in daftar:
berarti mencari string key dalam daftar yang isinya bilangan bulat (integer) semua, yaitu 2, 7, 4, 3, dan 9. Jelas tidak akan ditemukan. Untuk membuktikan bahwa key itu adalah string ubahlah sedikit pada baris terakhir
1 2 3 4 5 6
python
p r o d u k 2 . py 9 [ '9 ']
ditemukan
Selesai Dengan memberikan kurung siku pada variabel maka akan tampak bahwa key adalah string. Terlihat adanya kutip tunggal pada '9'. Lalu bagaimana mengubah key agar bertipe integer ? Gunakan fungsi int().
BAB 4.
TIPE DATA
27
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
daftar = { 2: 7: 4: 3: 9: } ' jeruk ' , ' mangga ' , ' pisang ' , ' jambu ' , ' apel ' ,
barang :
')
key = i n t ( key ) daftar : d a f t a r [ key ] ' Kode barang ' , key , ' tidak ditemukan '
Jalankan lagi.
1 2 3 4 5 6 7
Selesai Resapi kembali mengenai tipe data ini karena akan sering dijumpai nanti.
4.5
Waktu
Modul time digunakan untuk penanganan waktu. Kita kembali ke modus interaktif dulu untuk memudahkan latihan.
1 2 3 4
time
> > > t = time . l o c a l t i m e ( ) t i m e . s t r u c t _ t i m e ( tm_year = 2 0 1 0 , tm_mon=8 , tm_mday=20 , tm_hour =11 , tm_min=44 , =232 , t m _ i s d s t =0) tm_sec =47 , tm_wday=4 , tm_yday
Fungsi time.localtime() digunakan untuk mendapatkan waktu saat ini. Perhatikan variabel t di atas. Jika Anda ingin mengambil tahunnya:
1 2
BAB 4.
TIPE DATA
28
atau bulannya
1 2
1 2
> > > t . tm_mday 20 dan seterusnya hingga jam, menit, dan detiknya, ada di sana. Waktu juga bisa diwujudkan dalam bilangan pecahan (oat), sering disebut sebagai epoch atau Unix time.
1 2
> > > time . time ( ) 1282280699.2280381 Epoch ini adalah jumlah detik sejak 1 Januari 1970 jam 00:00:00 (GMT) hingga saat ini. Fungsi time.localtime() sebenarnya bisa diberikan masukan berupa epoch ini. Kita bisa mulai dengan angka 0.
1 2
> > > time . l o c a l t i m e ( 0 ) t i m e . s t r u c t _ t i m e ( tm_year = 1 9 7 0 , tm_mon=1 , tm_mday=1 , tm_hour =7 , tm_min=0 , t m _ i s d s t =0) Epoch 0 berarti 1 Januari 1970. Lalu mengapa tm_hour menunjukkan angka 7 ? Ini terkait dengan zona waktu (timezone) pada komputer yang berarti wilayah Jakarta (+7). Jika komputer Anda diset pada zona waktu Bali maka nilai tm_hour menjadi 8. Untuk mendapatkan epoch pada tanggal 17 Agustus 2010 maka bisa gunakan time.mktime(): tm_sec =0 , tm_wday=3 , tm_yday=1 ,
1 2
> > > t i m e . mktime ( ( 2 0 1 0 , 8 , 1 7 , 0 , 0 , 0 , 0 , 0 , 0 ) ) 1281978000.0 Dengan epoch kita bisa mendapatkan jumlah hari sejak 1 Agustus 2010 hingga 17 Agustus 2010.
1 2 3 4
> > > a w a l = t i m e . mktime ( ( 2 0 1 0 , 8 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ) ) > > > a k h i r = t i m e . mktime ( ( 2 0 1 0 , 8 , 1 7 , 0 , 0 , 0 , 0 , 0 , 0 ) ) > > > akhir 1382400.0 Itu adalah jumlah detiknya. Untuk mendapatkan hari kita perlu mengolahnya kembali.
awal
1 2
awal )
24
60
60
BAB 4.
TIPE DATA
29
Modul datetime
Menghitung hari lebih mudah menggunakan modul datetime.
1 2 3 4 5 6
datetime
import
date
> > > awal = date ( 2 0 1 0 , 8 , 1 ) > > > akhir = date (2010 ,8 ,17) > > > d = akhir > > > d . days 16 Dengan modul ini kita juga dapat menghitung jumlah detik sejak 16 Agustus 2010 jam 22:00 hingga 17 Agustus 2010 jam 10:00.
awal
1 2 3 4 5 6
datetime
import
datetime
> > > awal = d a t e t i m e ( 2 0 1 0 , 8 , 1 6 , 2 2 , 0 , 0 ) > > > akhir = datetime (2010 ,8 ,17 ,10 ,0 ,0) > > > d = akhir > > > d . seconds 43200 Untuk mendapatkan jumlah jamnya:
awal
1 2
60
60
Bab 5
Modularitas
Mari kita buat program yang menghitung nilai faktorial. Berikut ini contohnya:
5! 4! 3! 2! 1! 0! -1!
= = = = = = =
5 4 3 2 1 1 1
* * * *
4 3 2 1
* * * =
3 * 2 * 1 = 120 2 * 1 = 24 1 = 6 2
Dengan demikian rumus faktorial memiliki ketentuan: 1. Jika n < 2 maka n! = 1 2. n! = n * (n-1)! Selanjutnya kita akan buat program yang akan menanyakan nilai n dan menampilkan nilai faktorial-nya.
1 2 3 4 5 6 7 8 9 10 11 12 13
' Menghitung
n = raw_input ( ' n =
r a n g e ( 1 , n+1) : i f) 30
= %d ' % ( n ,
BAB 5.
MODULARITAS
31
Jalankan.
1 2 3 4 5 6 7 8 9 10
python
f a k t o r i a l 1 . py nilai faktorial
Menghitung n = 2 2! = 2 n = 3 3! = 6 n = 4 4 ! = 24 n = $
Selesai sudah.
gras (graphical user interface / GUI). Tentu saja tidak ada lagi yang namanya raw_input() dan print, karena ia digunakan untuk lingkungan teks (console). Juga tidak ada lagi while karena GUI sudah mengatur perulangannya. Lalu apa yang diambil untuk mengambil source faktorial ? saja yang perlu di-copy-paste di source GUI nanti. raw_input() melainkan dari komponen GUI. Di sini mulai terasa source faktorial di atas tidak modular karena menyulitkan copy-paste. Kesulitan yang dimaksud adalah source faktorial menyatu Faktorial adalah contoh sederhana, dengan source yang mengurus tampilan. rumit ? Baris 6 - 12 Anda juga harus menye-
suaikan variabel n yang menjadi masukannya, karena n tidak lagi berasal dari
bagaimana kalau nanti Anda diminta membuat rumus lainnya yang jauh lebih
5.1
Membuat Fungsi
Sudah saatnya Anda mengenal pembuatan fungsi. Dengannya Anda pisahkan urusan menghitung nilai faktorial dengan urusan input dan output tampilannya.
1 2 3 4 5 6 7 8 9 10 11 12 13
def if
' Menghitung
nilai ')
faktorial '
n = raw_input ( ' n = n:
BAB 5.
MODULARITAS
32
14 15 16 17
n = int (n)
break
print
$ n = 5
hasil
'%d !
Jalankan.
1 2 3 4 5 6 7 8 9 10 11
python
f a k t o r i a l 2 . py nilai faktorial
Menghitung 5 ! = 120 n = 2 2! = 2 n = 1 1! = 1 n =
1
= 1
1!
n =
Hasilnya memang sama saja, namun kini source faktorial lebih mudah dibaca dan di-copy-paste. keluar dari fungsi. Apa ini sudah cukup modular ? Jawabannya belum. Perhatikan juga penggunaan return yang membuat alur
5.2
Membuat Modul
Copy-paste source seperti itu tentu saja lebih sulit ketimbang copy-paste lenya. Oleh karena itu Anda perlu membuat source itu menjadi modul faktorial yang berarti namanya menjadi faktorial.py sehingga program lain yang membutuhkannya cukup menggunakannya seperti ini (contoh):
1 2 3
from n = 5 print
faktorial
import
faktorial
f a k t o r i a l (n)
1 2 3 4 5 6
from
faktorial
import
BAB 5.
MODULARITAS
33
2 3 4 5 6 7 8 9 10
' Menghitung
nilai ')
faktorial '
True : n:
n = raw_input ( ' n =
Jalankan.
1 2 3 4 5 6 7
python
c o b a f a k t o r i a l . py Nilai Faktorial
Menghitung
Hasilnya tetap sama, namun kini Anda lebih mudah copy-paste source fungsi faktorial() karena cukup faktorial.py yang di-copy ke direktori program lainnya. Untuk mencoba modul faktorial Anda perlu membuat le lainnya yaitu cobafaktorial.py. Agar lebih praktis, mengapa tidak disatukan saja ? Benar, alangkah praktisnya jika source untuk menguji fungsi faktorial() juga berada di le yang sama. Namun Anda perlu membuat sedikit perubahan agar saat
1 2 3 4 5 6 7 8 9 10 11 12 13 14
__name__ == True :
' Menghitung
n = raw_input ( ' n = n:
n = int (n)
BAB 5.
MODULARITAS
34
15 16
print
$ python
hasil
'%d !
Perhatikan bahwa __name__ adalah name yang diawali dan diakhiri oleh dua underscore. Begitu juga dengan __main__. Kemudian jalankan.
1 2 3 4 5 6 7
f a k t o r i a l . py nilai faktorial
Menghitung n = 3 3! = 6 n = 4 4 ! = 24 n =
if __name__ == '__main__':
Dengan baris ini Python diberitahu bahwa source dibawahnya hanya dijalankan jika faktorial.py merupakan program utama. Kalau faktorial.py sebagai modul maka ia tidak dijalankan. Anda juga masih bisa menggunakan cobafaktorial.py seperti biasa.
python
c o b a f a k t o r i a l . py
5.3
Search Path
Meng-copy faktorial.py ke berbagai direktori program yang membutuhkan tentu saja merepotkan. Apalagi bila ada perubahan source pada faktorial.py, maka Anda harus menyebarkannya lagi. Ada banyak direktori dimana Python akan mencari modul yang dipanggil. Untuk mengetahui direktori mana saja yang terdaftar gunakan variabel sys.path. Masuklah ke modus interaktif untuk melihatnya.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sys
> > > s y s . path ' / u s r / l i b / python2 . 6 ' , ' / u s r / l i b / p y t h o n 2 . 6 / p l a t l i n u x 2 ' , ' / u s r / l i b / p y t h o n 2 . 6 / l i b t k ' , ' / u s r / l i b / p y t h o n 2 . 6 / l i b o l d ' , ' / u s r / l i b / p y t h o n 2 . 6 / l i b d y n l o a d ' , ' / u s r / l i b / p y t h o n 2 . 6 / d i s t p a c k a g e s ' , ' / u s r / l i b / p y t h o n 2 . 6 / d i s t p a c k a g e s / PIL ' , ' / u s r / l i b / p y t h o n 2 . 6 / d i s t p a c k a g e s / g s t ' / u s r / l i b / pymodules / python2 . 6 ' , ' / u s r / l i b / p y t h o n 2 . 6 / d i s t p a c k a g e s / g t k ' / u s r / l i b / pymodules / python2 . 6 / gtk
0.10 ' ,
' / u s r / l o c a l / l i b / p y t h o n 2 . 6 / d i s t p a c k a g e s ' ]
BAB 5.
MODULARITAS
35
Pertama kali Python akan mencari di direktori dimana program utama berada. Selanjutnya ia akan mencari di direktori lainnya seperti yang Anda lihat di atas. Lalu dimana sebaiknya faktorial.py diletakkan ? Karena meletakkannya tidak melalui instalasi paket Debian, maka saya sarankan diletakkan di direktori
/usr/local/lib/python2.6/dist-packages
Anda bisa menyalinnya menggunakan perintah
sudo
cp
f a k t o r i a l . py
/ u s r / l o c a l / l i b / python2 . 6 / d i s t
s u d o mv
f a k t o r i a l . py
/ u s r / l o c a l / l i b / python2 . 6 / d i s t
packages Jika Anda punya direktori lain, Anda bisa tambahkan pada sys.path terlebih dahulu sebelum import.
1 2 3
Bab 6
Fungsi
6.1
Fungsi faktorial() sudah bekerja dengan baik. Tapi mungkin source-nya terlalu panjang. Cobalah ubah menjadi seperti di bawah ini pada faktorial.py.
1 2 3 4
def if
return
f a k t o r i a l ( a 1)
Jauh lebih esien bukan? Teknik seperti ini disebut juga sebagai rekursif. Pertama kali yang harus diperhatikan dalam pembuatan fungsi rekursif adalah batas kedalaman. Batas ini ditunjukkan pada baris 2-3. Rekursif yang tidak memiliki batas kedalaman akan menampilkan pesan kesalahan.
6.2
Format Uang
Untuk menampilkan uang biasanya ada pemisah ribuannya. Untuk Indonesia pemisah ribuan adalah titik, sedangkan pemisah pecahan adalah koma. Untuk kebutuhan tersebut kita bisa gunakan modul locale.
1 2 3 4 5
> > > import > > > > > >
locale
l o c a l e . s e t l o c a l e ( l o c a l e . LC_ALL, ' id_ID . UTF 8 ' ) l o c a l e . format ( '%.2 f ' , 10000 , True )
' id_ID . UTF8 ' '10.000 ,00 ' Jika pecahannya tidak ingin ditampilkan:
1 2
10000 ,
True )
'10.000 ' Namun jika saat setlocale Anda menjumpai pesan kesalahan seperti ini:
36
BAB 6.
FUNGSI
37
1 2 3 4 5
( most
recent line
call 1,
"< s t d i n >" ,
i n <module>
Berarti Anda perlu memasang format id_ID.UTF-8 pada sistem. Keluarlah dari modus interaktif dan jalankan:
1 2 3 4
sudo
locale
g e n
Generating Generation
locales . . . complete .
id_ID . UTF 8 . . .
Kalau sudah cobalah kembali contoh pemisah ribuan di atas. Kini saatnya membuat fungsi untuk menampilkan nilai uang ini. Kita namakan dengan uang(). Fungsi ini akan menerima dua masukan: 1. Nilai uang yang akan ditampilkan 2. Jumlah digit pecahan yang akan ditampilkan, default-nya adalah 2 digit pecahan. Karena uang() adalah fungsi umum yang akan banyak digunakan di berbagai program, ada baiknya kita simpan sebagai modul bernama uang.py.
1 2 3 4 5 6 7 8 9 10 11 12
import
uang ( n i l a i ,
l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n ,
__name__ ==
'__main__ ' :
Cobalah.
1 2 3 4
python
uang . py
'%%.%df' % pecahan
BAB 6.
FUNGSI
38
Itu adalah formatting seperti yang dijelaskan di halaman 16. Butuh fungsi yang lebih cerdas ? Misalkan dengan sifat seperti ini: 1. Jika pecahan tidak disebutkan maka modus otomatis berlaku. Otomatis yang dimaksud adalah bila tipe data variabel nilai adalah bilangan bulat (integer) maka pecahan tidak ditampilkan. Selain kondisi itu berarti dianggap bilangan pecahan (oat) maka pecahan ditampilkan sebanyak 2 digit. 2. Selain kondisi di atas maka nilai uang ditampilkan seperti biasa sesuai jumlah pecahan yang disebutkan. Masih di uang.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import def if
uang ( n i l a i , pecahan
is
p e c a h a n=None ) : None :
pecahan = 0 pecahan = 2
if
__name__ ==
'__main__ ' :
Jalankan lagi.
BAB 6.
FUNGSI
39
2. Bila variabel tanda tidak diisi maka mata uang diambil dari sistem, menggunakan module locale.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
import is
IntType
p e c a h a n=None ) : None :
if else return def if return if print print print print print print
pecahan = 0 pecahan = 2
is
p e c a h a n=None ,
t a n d a=None ) :
'%s%s ' % ( t a n d a ,
__name__ ==
'__main__ ' :
ribu (10000) ribu (10000.3) ribu (10000.5 , uang ( 1 0 0 0 0 . 7 ) uang ( 1 0 0 0 0 . 7 , uang ( 1 0 0 0 0 . 7 , 2, '$ ' ) t a n d a= ' $ ' ) 4)
Perhatikan fungsi tambahan ribu(). Fungsi ini sebenarnya salinan dari fungsi uang() sebelumnya. Mengapa tidak langsung mengubah fungsi uang() seperti biasanya ? Tujuannya adalah kita tidak ingin mengganggu algoritma yang sudah berjalan baik itu. Sehingga bila ada kekeliruan pada algoritma di uang(), area pencarian kesalahan bisa diminimalisir. Perhatikan juga baris terakhir,
BAB 6.
FUNGSI
40
Karena modul uang ini merupakan modul umum yang bisa digunakan oleh banyak aplikasi, maka letakkanlah uang.py di /usr/local/lib/python2.6/distpackages agar bisa digunakan oleh program lainnya. Lalu uji keberadaannya di modus interaktif.
1 2 3
uang
import
uang
Bab 7
Database
Database atau penyimpan data biasa digunakan untuk aplikasi bisnis seperti kasir (point of sales), accounting, payroll, dsb. Pasanglah
sudo
a p t g e t
install
postgresql
Superuser di PostgreSQL adalah postgres. Nama ini tercantum di sistem Linux maupun di sistem PostgreSQL itu sendiri. Pasca pemasangan user postgres tidak memiliki password. Jadi gunakanlah sudo:
sudo
su
Masukkan password user yang Anda gunakan ketika login pertama kali. Kini Anda telah menjadi root, dan mulailah sebagai user postgres:
# su
user Linux dan user PostgreSQL sama maka tidak perlu password untuk login ke PostgreSQL server. Sekarang mulailah membuat user database:
1 2 3 4 5 6
ilham for new be be be a role : superuser ? to to ( y/n ) n databases ? more new ( y/n ) (
allowed allowed
create create
roles ?
y/n )
Isilah password dengan 1234. Saat diketik password tidak akan ditampilkan. Selebihnya user ilham ini tidak dizinkan sebagai superuser, tidak diizinkan membuat database, dan tidak diizinkan membuat user. Untuk menghapusnya:
dropuser
ilham 41
BAB 7.
DATABASE
42
createdb
ilham
totalindo
Ini artinya kita membuat database bernama totalindo yang dimiliki oleh user ilham. Untuk menghapusnya:
dropdb
totalindo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
psql Type
++++ postgres | p o s t g r e s | UTF8 | id_ID . UTF 8 | t e m p l a t e 0 | p o s t g r e s | UTF8 | id_ID . UTF 8 | t e m p l a t e 1 | p o s t g r e s | UTF8 | id_ID . UTF 8 | t o t a l i n d o | ilham | UTF8 | id_ID . UTF 8 |
(4 rows )
1 2 3
p o s t g r e s= # \q $ whoami postgres Anda masih sebagai user postgres, keluarlah dengan perintah logout atau tekan Ctrl-D:
1 2 3
logout
# whoami root Kini Anda sebagai root, keluarlah lagi dengan perintah logout agar kembali sebagai user biasa:
1 2 3
# logout $ whoami sugiana Sekarang kita login ke database totalindo sebagai user ilham:
1 2 3 4
psql
ilham for
totalindo ilham :
localhost
user
BAB 7.
DATABASE
43
5 6 7
Type
for
help .
t o t a l i n d o= > Perhatikan di sini kita menggunakan option yang lengkap di psql, padahal pada saat sebagai user postgres kita cukup mengetikkan psql saja. Apa bedanya ? psql saja tanpa option apapun berarti: 1. Usernya sesuai user Linux. 2. Nama database sesuai nama user. 3. Koneksi melalui jalur UNIX socket, tidak melalui network.
7.1
Tabel
Panduan membuat tabel: 1. Harus memiliki PRIMARY KEY, yaitu sebuah / beberapa eld yang menjadi identitas record. 2. Sebisa mungkin setiap eld memiliki DEFAULT value dimana kalau tidak diisi maka nilai default digunakan. 3. Sebisa mungkin setiap eld NOT NULL yang berarti harus diisi. Mulailah membuat tabel pegawai di psql menggunakan perintah CREATE TABLE.
1 2 3 4 5 6 7 8 9
t o t a l i n d o= > CREATE TABLE p e g a w a i ( t o t a l i n d o (> t o t a l i n d o (> t o t a l i n d o (> id serial NOT NULL, t o t a l i n d o (> nama v a r c h a r ( 3 0 ) NOT NULL,
t g l _ l a h i r DATE NOT NULL, ) ; will create implicit sequence " for serial for column " pegawai . i d " create implicit
NOTICE : CREATE TABLE / PRIMARY KEY w i l l " pe g aw a i_ p ke y " table t o t a l i n d o= > Perhatikan prompt
totalindo=>
dan bedakan dengan prompt
totalindo(>
BAB 7.
DATABASE
44
CREATE TABLE
menjadi
create table
Sekarang kita lihat daftar tabel yang ada di database totalindo ini menggunakan perintah \dt.
1 2 3 4 5 6 7 8
t o t a l i n d o= > \ dt List Schema public (1 row ) | | of relations | | Type table | | Owner ilham Name pegawai
+++
t o t a l i n d o= >
Perintah yang diawali backslash ( \ ) adalah perintah program psql, bukan jenis query seperti CREATE TABLE, SELECT, dst. Lalu lihat struktur tabelnya menggunakan perintah \d diikuti nama tabel.
1 2 3 4 5 6 7 8 9 10 11
t o t a l i n d o= > \d Column |
Table
++
id nama tgl_lahir Indexes : " pe g aw ai _ pk e y " PRIMARY KEY, t o t a l i n d o= > btree ( id ) | | | integer character date varying (30) | | | not not not null null null default n e x t v a l ( ' pegawai_id_seq ' : : r e g c l a s s )
BAB 7.
DATABASE
45
Perhatikan kolom / eld id yang bertipe integer, padahal sebelumnya kita tulis bertipe serial. Mengapa demikian ? Tipe serial hanyalah cara cepat untuk membuat sebuah eld menjadi autoincrement (nomor urut). membuat: 1. Tipenya menjadi integer (bilangan bulat). 2. Memiliki default value nomor urut berikutnya. tersimpan dalam sebuah sequence. Apa itu sequence ? Nomor urut berikutnya Jadi pendenisian suatu eld menjadi serial akan
7.1.1
Sequence
butuhan nomor urut. Pada contoh di atas sequence bernama pegawai_id_seq otomatis terbentuk saat eld id pada tabel pegawai didenisikan sebagai serial. Data pada sequence bisa dilihat sebagaimana tabel menggunakan perintah SELECT.
1 2 3 4 5 6
F R O M pegawai_id_seq | | is_called f
; | |
last_value 1
+++
1 2 3 4
F R O M pegawai ;
tgl_lahir
++
rows ) Tampak tabel pegawai masih kosong, mari tambah datanya.
1 2 3
t o t a l i n d o= > INSERT INTO p e g a w a i ( nama , t g l _ l a h i r ) totalindo > VALUES ( ' Bummi Dwi INSERT 0 1 Putera ' , '1985 8 17 ') ;
Perhatikan perintah di baris pertama tidak diakhiri dengan titik koma yang berarti perintah belum berakhir dan dilanjutkan di baris berikutnya. Perhatikan juga prompt pada baris kedua menjadi
totalindo->
yakni menggunakan karakter minus ( - ) yang berarti baris ini merupakan kelanjutan dari baris sebelumnya. Jika Anda salah dalam menuliskan perintah di baris pertama dan terlanjur menekan Enter, akhiri saja dengan titik koma di baris kedua. Kemudian mulai lagi dari awal. Berikut ini contoh kesalahan yang bisa saja terjadi.
BAB 7.
DATABASE
46
1 2 3 4 5 6
t o t a l i n d o= > INSERT INT totalindo > ERROR: LINE 1: ; error ^ t o t a l i n d o= > at syntax
INSERT INT
p e g a w a i ( nama , t g l _ l a h i r )
Untuk mengulangi perintah sebelumnya tekan tombol panah atas. Kembali ke tabel pegawai yang sudah kita isi dengan perintah INSERT. Sekarang lihat hasilnya.
1 2 3 4 5
F R O M pegawai ; | tgl_lahir
1 2 3 4 5 6
F R O M pegawai_id_seq | | is_called t
last_value 1
++
Bandingkan dengan nilai-nilai pegawai_id_seq pada SELECT sebelum INSERT. Yang perlu diperhatikan adalah eld is_called dimana sebelumnya f (False) kini menjadi t (True). Ini artinya sequence sudah digunakan agar nextval() berikutnya tahu bahwa nilai berikutnya menghasilkan last_value + 1 = 2. Sekarang lanjut dengan pegawai berikutnya.
1 2 3 4 5 6 7 8 9
t o t a l i n d o= > INSERT INTO p e g a w a i ( nama , t g l _ l a h i r ) totalindo > VALUES ( ' A r i e f INSERT 0 id | 1 t o t a l i n d o= > SELECT nama S e t i a d i ' , '1972 5 2 ') ;
F R O M pegawai ; | tgl_lahir
t o t a l i n d o= > SELECT
F R O M pegawai_id_seq
BAB 7.
DATABASE
47
2 3 4 5 6
| |
last_value 2
| |
is_called t
| | 1
+++
Kini last_value menjadi 2, sehingga nextval() berikutnya last_value + 1 = 3. Mudah-mudahan bisa dipahami. Mengenai eld tgl_lahir, bolehkah diisi menggunakan format Indonesia yaitu dengan urutan tanggal-bulan-tahun ? Jawabannya boleh, hanya ada yang perlu diperhatikan pada kongurasi PostgreSQL, yaitu pada le
/etc/postgresql/8.4/main/postgresql.conf
Coba lihat dengan text editor.
sudo
nano
/ e t c / p o s t g r e s q l / 8 . 4 / main / p o s t g r e s q l . c o n f
sudo
/ e t c / i n i t . d/ p o s t g r e s q l
8.4
restart
Perlu Anda ketahui, format dmy otomatis Anda dapatkan bila saat instalasi Ubuntu menggunakan bahasa Indonesia. Cobalah untuk menambah data pegawai lagi dengan tanggal lahir berformat tanggal-bulan-tahun.
1 2 3 4 5 6 7 8 9 10
t o t a l i n d o= > INSERT INTO p e g a w a i ( nama , t g l _ l a h i r ) totalindo > VALUES ( ' Cecep INSERT 0 id | 1 t o t a l i n d o= > SELECT nama Zahrudin ' , '1 6 1972 ') ;
F R O M pegawai ; | tgl_lahir
BAB 7.
DATABASE
48
1 2 3 4 5 6 7
1 2 3 4 5
F R O M pegawai W H E R E id = 1; | tgl_lahir
1 2 3 4 5 6 7 8
t o t a l i n d o= > SELECT
F R O M pegawai t g l _ l a h i r ) = 1972
totalindo > W H E R E date_part ( ' year ' , totalindo > ORDER BY t g l _ l a h i r ; id | nama | tgl_lahir
1 2 3 4 5 6 7 8
7.1.2
Mengubah Struktur
Pegawai sebelumnya tampak sebagai laki-laki, terlihat dari namanya. Cobalah untuk memasukkan nama perempuan.
1 2 3
t o t a l i n d o= > INSERT INTO p e g a w a i ( nama , t g l _ l a h i r ) totalindo > VALUES ( ' N i t a INSERT 0 1 Pandria ' , '19 9 1976 ') ;
BAB 7.
DATABASE
49
Manusia bisa jadi memahami mana nama laki-laki dan mana perempuan. Namun komputer mengalami kesulitan bila mengandalkan nama. Oleh karena itu kita perlu menambah eld pria yang bertipe logika (boolean). Kita membutuhkan perintah ALTER TABLE di sini.
1 2
b o o l e a n NOT NULL
Seperti dijelaskan pada sesi Python sebelumnya, boolean hanya bisa diisi dengan dua nilai, bisa True atau False. Kita menamakan eld ini dengan pria dengan begitu nilai default-nya adalah True. Jika eld pria False berarti pegawai tersebut perempuan. Sekarang kita lihat isi tabel pegawai setelah penambahan eld di atas.
1 2 3 4 5 6 7 8
+++ 2 | Arief Setiadi | 1972 05 02 | t 1 | Bummi Dwi P u t e r a | 1985 08 17 | t 3 | Cecep Z a h r u d i n | 1972 06 01 | t 4 | Nita Pandria | 1976 09 19 | t
(4 rows ) Perhatikan eld pria, tampak semua tertulis t yang artinya True. Apa data tabel ini sudah benar? Jelas belum. Nita Pandria adalah perempuan, sedangkan tabel pegawai menyatakannya laki-laki. Kita perlu mengubahnya dengan perintah UPDATE.
1 2 3 4 5 6 7 8 9 10
pria = False W H E R E id = 4;
+++ 2 | Arief Setiadi | 1972 05 02 | t 1 | Bummi Dwi P u t e r a | 1985 08 17 | t 3 | Cecep Z a h r u d i n | 1972 06 01 | t 4 | Nita Pandria | 1976 09 19 | f
(4 rows ) Kita sudah membuat record dengan INSERT, menampilkannya dengan SELECT, dan mengubahnya dengan UPDATE. Kini kita coba untuk menghapusnya dengan DELETE FROM yaitu pegawai dengan id 2.
1 2 3 4 5
+++
BAB 7.
DATABASE
50
6 7 8 9
(3
1 3 4
| | |
Putera
| | |
| | |
t t f
Zahrudin Pandria
rows ) Jika Anda kurang suka dengan eld pria yang bertipe boolean bisa dihapus
1 2
t o t a l i n d o= > ALTER TABLE p e g a w a i DROP p r i a ; ALTER TABLE Anda masih kurang berkenan dengan struktur tabel pegawai ? hapus dengan perintah DROP TABLE. Silahkan
1 2
t o t a l i n d o= > DROP TABLE p e g a w a i ; DROP TABLE SELECT, INSERT, UPDATE, dan DELETE adalah hal yang terkait dengan record / baris data sehingga disebut sebagai Data Manipulation Language (DML). Sedangkan CREATE, ALTER, dan DROP terkait dengan struktur tabel sehingga disebut sebagai Data Denition Language (DDL).
7.2
View
Dibutuhkan sebuah laporan yang menampilkan data pegawai beserta usianya. Untuk mendapatkan usia kita bisa gunakan rumus:
1 2 3 4 5 6 7 8
t o t a l i n d o= > SELECT i d , id 2 3 4 1 (4 | | | | | Arief Cecep Nita nama Setiadi Zahrudin Pandria Putera
nama , | | | | |
now ( )
tgl_lahir F R O M
p e g a w a i ORDER BY t g l _ l a h i r ; ? column ? 13957 13927 12356 9102 days days days days 11:21:54.083022 11:21:54.083022 11:21:54.083022 11:21:54.083022
++
Bummi Dwi
rows ) Kolom ketiga berisi usia, tapi PostgreSQL tidak tahu harus dinamakan apa,
sehingga jadilah bernama ?column?. Ada baiknya kita berikan nama usia.
1 2 3 4 5 6
nama , | | |
now ( )
tgl_lahir usia
AS
usia
++
13957 13927 days days 11:28:34.001142 11:28:34.001142
BAB 7.
DATABASE
51
7 8 9
(4
4 1
| |
Nita
Pandria Putera
| |
12356 9102
days days
11:28:34.001142 11:28:34.001142
Bummi Dwi
rows ) Satuan usia menggunakan hari tentu saja kurang nyaman dibaca. Sebaiknya
1 2 3 4 5 6 7 8 9 10
from
++
Bummi Dwi
rows ) Perintah SQL (query) di atas dapat disimpan dalam sebuah VIEW.
1 2 3 4
t o t a l i n d o= > CREATE VIEW v_pegawai AS SELECT i d , ext ract ( year AS usia from now ( ) )
nama ,
from
tgl_lahir )
F R O M p e g a w a i ORDER BY t g l _ l a h i r ; CREATE VIEW Selanjutnya kita tampilkan VIEW tadi layaknya sebuah tabel.
1 2 3 4 5 6 7 8
F R O M v_pegawai ; | | | | | usia 38 38 34 25
++
Zahrudin Pandria Putera
Bummi Dwi
rows ) Penamaan VIEW tidak harus berawalan v_. Penggunaan awalan itu un-
tuk membedakan VIEW dengan tabel sebenarnya. Meski ia dapat di-SELECT namun VIEW tidak dapat di-INSERT begitu saja. Walau v_pegawai sudah mengandung ORDER BY, kita masih bisa menggunakan ORDER BY saat SELECT, misalnya diurut berdasar nama.
1 2
BAB 7.
DATABASE
52
3 4 5 6 7 8
++
2 1 3 4 (4 | | | | Arief Cecep Nita Setiadi Putera Zahrudin Pandria | | | | 38 25 38 34 Bummi Dwi
rows )
7.3
Jika tabel pegawai benar-benar Anda hapus, sebaiknya mulailah membuatnya lagi dan mengisinya. Selain langsung melalui psql modus interaktif, Anda bisa membuatnya dulu dalam sebuah le pegawai.sql.
1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE varchar NOT NULL date NOT NULL NOT NULL DEFAULT true PRIMARY KEY
pegawai ( (30) id serial , nama pria , tgl_lahir , boolean ( id ) ) ;
true
) ;
1 2
t o t a l i n d o= > \i implicit
pegawai . s q l NOTICE : CREATE TABLE for will create column " pegawai_id_seq " NOTICE : serial
implicit
index
4 5 6 7 8
BAB 7.
DATABASE
53
t o t a l i n d o= > \i
/home/ s u g i a n a / p e g a w a i . s q l
Sesuaikan dengan direktori dimana Anda menyimpan pegawai.sql. Mudahmudahan Anda mengerti maksudnya. Kini backup database totalindo dalam sebuah le berformat tar.
$ pg_dump
ilham
totalindo
localhost
totalindo . sql . tar Mulailah untuk latihan restore, yaitu dengan membuat database lain bernama totalindo_1 (lihat pembuatan database di awal bab ini). Lalu jalankan:
pg_restore
ilham
localhost
totalindo_1
totalindo . sql . tar Anda juga bisa backup menggunakan format teks biasa (plaintext):
$ pg_dump sql
ilham
totalindo
localhost
totalindo .
psql
ilham
totalindo_1
localhost
totalindo . sql
Manfaat penggunaan plaintext adalah Anda bisa mengubah le totalindo.sql menggunakan teks editor biasa. Namun penggunaan format ini terkadang menimbulkan masalah saat restore bila ada karakter aneh, yaitu karakter dengan nomor ASCII lebih besar dari 126.
7.3.1
Encoding
PostgreSQL versi terdahulu biasanya menggunakan SQL_ASCII sebagai encoding character. Anda bisa lihat dengan perintah \l di psql.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
psql
ilham for
template1 help .
localhost
psql Type
++++ postgres | p o s t g r e s | UTF8 | id_ID . UTF 8 | template0 | p o s t g r e s | UTF8 | id_ID . UTF 8 | template1 | p o s t g r e s | UTF8 | id_ID . UTF 8 | totalindo | ilham | UTF8 | id_ID . UTF 8 | totalindo_1 | ilham | UTF8 | id_ID . UTF 8 |
(5 rows )
BAB 7.
DATABASE
54
Perhatikan di versi ini default encoding adalah UTF8. Jika Anda membackup, perhatikanlah encoding ini. Misalkan menggunakan SQL_ASCII, maka di database yang baru pun Anda sebaiknya tetap menggunakan encoding yang sama. Sebagai latihan kembalilah ke konsole, lalu buat database totalindo_2 dengan encoding SQL_ASCII.
1 2 3
$ $
sudo
su postgres
# su
createdb
ilham
template0
sql_ascii
totalindo_2
Kembali ke psql dan lihat daftar database. Karena masih sebagai user postgres maka cukup ketik psql.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
psql Type
++++ postgres | p o s t g r e s | UTF8 | id_ID . UTF 8 | template0 | p o s t g r e s | UTF8 | id_ID . UTF 8 | template1 | p o s t g r e s | UTF8 | id_ID . UTF 8 | totalindo | ilham | UTF8 | id_ID . UTF 8 | totalindo_1 | ilham | UTF8 | id_ID . UTF 8 | totalindo_2 | ilham | SQL_ASCII | id_ID . UTF 8 |
(6 rows ) Mengenai cara backup dan restore lainnya sama seperti sebelumnya. Mengapa kita perlu memperhatikan encoding ? Bila database yang Anda backup tidak mengandung karakter aneh (ASCII > 126) maka bisa dengan nyaman di-restore dari ke UTF8. Namun bila mengandung ASCII > 126 maka restore ke UTF8 dari sumber SQL_ASCII bisa menimbulkan masalah.
7.4
Fungsi
Sama seperti Python, PostgreSQL juga mengenal fungsi. Contoh fungsi bawaan (built-in) adalah now().
1 2 3 4 5
2010 08 24 1 6 : 0 6 : 5 8 . 6 7 8 0 5 6 + 0 7
(1 row ) Keunikan fungsi pada PostgreSQL adalah pada nama dan susunan masukannya (input parameter). Perhatikan contoh rata kanan berikut ini.
BAB 7.
DATABASE
55
1 2 3 4 5
A (1 row ) Fungsi lpad() berguna untuk memberi spasi di sebelah kiri. Jumlah spasi
ditambah jumlah karakter string 'A' tidak boleh melebihi 5. Jadi pada contoh di atas jumlah spasi di sebelah kiri 'A' sebanyak 4 buah. Untuk membuktikan jumlah karakternya adalah 5 maka kita gunakan fungsi length().
1 2 3 4 5
l e n g t h ( l p a d ( ' A' , 5 ) ) ;
5 (1 row ) Sekarang kita membutuhkan awalan '0' pada sebuah nomor, misalnya '00001'. Ini lazim kita temui pada beberapa laporan. Fungsi apa yang akan digunakan. Jawabannya masih menggunakan fungsi lpad().
1 2 3 4 5
00001 (1 row ) Di sinilah letak keunikan sebuah fungsi pada PostgreSQL. Fungsi lpad() bisa menerima masukan dua buah atau tiga buah. Jika dua buah saja yang digunakan maka lpad() akan mengawalinya dengan spasi. Namun jika tiga buah masukan, maka lpad() akan mengawalinya sesuai dengan karakter pada masukan ketiga. Cermatilah baik-baik. Jadi bila kita ingin membuat beberapa fungsi dengan nama sama maka harus dibedakan dengan: 1. Jumlah masukannya. 2. Jika jumlah masukannya sama maka bedakan tipe data setiap masukan tersebut.
7.4.1
PL/pgSQL
Salah satu yang membuat PostgreSQL begitu mudah dikembangkan adalah fungsi tersebut bisa ditulis dalam berbagai bahasa. bahasa yang didukungnya adalah plpgsql. Dimana Anda bisa meSecara default nulis fungsi dalam bahasa pgSQL, Python, Perl, hingga Tcl. terlebih dahulu pada database yang dimaksud.
sudo
su
postgres
" createlang
plpgsql
totalindo "
BAB 7.
DATABASE
56
psql
ilham
totalindo
localhost
sedangkan yang kedua untuk text editor vi. Tentu saja Anda bisa menggunakan gedit. Text editor akan kita gunakan untuk membuat le-le *.sql. Sekarang kita akan membuat fungsi sederhana hello(), tanpa masukan apapun, disimpan dalam le hello.sql.
1 2 3 4 5 6 7 8
CREATE OR AS
$$ BEGIN RETURN
RETURNS t e x t
LANGUAGE p l p g s q l
END
$$ ;
' Hello
world . ' ;
1 2
hello . sql
1 2 3 4 5
hello () ;
Transaksi Perbankan
Kita sudah punya tabel pegawai dimana setiap pegawai dianggap sebagai nasabah pada perusahaan ini. Saat mendapat gaji saldonya bertambah. Saat ambil gaji saldonya berkurang. ke perusahaan. Saat kas bon (pinjam) saldonya juga berku-
rang. Jadi saldo juga bisa negatif yang berarti pegawai tersebut punya hutang Pertama kita butuh eld saldo pada tabel pegawai. pegawai
ALTER TABLE
ADD
saldo
0;
Kemudian untuk mencatat aktivitas transaksi kita memerlukan tabel transaksi berikut ini.
1 2 3
REFERENCES p e g a w a i ,
BAB 7.
DATABASE
57
4 5 6 7 8
) ;
tgl ket
nominal saldo
timestamp NOT NULL DEFAULT varchar NOT NULL float NOT NULL float NOT NULL
(100) , ,
now ( ) ,
Misalkan ID pegawai 1 bernama Bummi Dwi Putera mendapat gaji untuk bulan Nopember 2010 sebesar Rp 3.000.000,- maka query-nya adalah:
1 2 3 4 5 6
s a l d o = s a l d o + 3000000 W H E R E id = 1;
Ya, kita butuh dua query untuk sebuah transaksi. Mungkin ada pertanyaan, untuk apa eld saldo pada tabel transaksi ? Field ini digunakan untuk memudahkan pencetakan buku tabungan yang biasanya mencantumkan saldo pada setiap transaksi. Kalau diperhatikan, transaksi tersebut membutuhkan 3 parameter masukan: 1. ID pegawai 2. Keterangan transaksi 3. Nominal transaksi Kalau transaksi ini dikemas dalam sebuah fungsi, lalu apa keluarannya ? Keluaran atau output yang paling tepat adalah ID transaksi. Query-nya bisa kita peroleh dengan cara berikut ini.
SELECT
1;
1 2 3 4 5 6 7
id
pid
tgl
+++ 1 | 1 | 2010 10 22 1 7 : 5 8 : 0 1 . 8 6 |
ket | nominal | saldo
++ GAJI 11 2010 | 3 0 0 0 0 0 0 | 3 0 0 0 0 0 0
Selanjutnya kita buat functransaksi.sql untuk fungsi dimaksud.
1 2 3 4
REPLACE FUNCTION
BAB 7.
DATABASE
58
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
RETURNS
LANGUAGE p l p g s q l
AS
integer
DECLARE BEGIN
UPDATE SET WHERE INSERT INTO SELECT FROM WHERE SELECT INTO FROM ORDER BY DESC LIMIT
id , pid , tid , pegawai id rec transaksi id 1; RETURN r e c . i d ;
nominal ,
i d = p_pid ;
END
$$
1 2
functransaksi . sql
1 2 3 4 5
t r a n s a k s i ( 1 , 'BON' , 2 0 0 0 0 0 ) ;
2 (1 row ) Perhatikan nominal berisi nilai negatif yang artinya mengurangi saldo. Sekarang lihat keseluruhan transaksi.
1 2 3 4
saldo
+++++
1 | 1 | 2010 10 22 | 3000000 17:58:01.869139 | GAJI 11 2010 |
3000000
BAB 7.
DATABASE
59
5 6
(2
| rows )
2010 10 22 | 2800000
18:20:08.845091
| BON
200000
Dilihat dari aspek kecepatan, query SELECT terhadap tabel transaksi bisa dihindari. Maklumlah tabel transaksi ini tergolong cepat pertumbuhan recordnya. Jadi untuk mendapatkan ID transaksi kita perlu menggunakan sequence yang dimiliki tabel transaksi tersebut. Ubahlah functransaksi.sql menjadi berikut ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
REPLACE FUNCTION
s a l d o = s a l d o + p_nominal i d = p_pid ;
nominal ,
tid ,
pegawai i d = p_pid ;
END
$$
RETURN t i d ;
Restore.
1 2
transaksi . sql
1 2 3 4 5
t r a n s a k s i ( 5 , ' GAJI
3 (1 row )
BAB 7.
DATABASE
60
1 2 3 4 5 6 7
saldo
+++++
1 2 3 (3 | | | rows ) 1 1 5 | | | 2010 10 22 | | | 3000000 18:20:08.845091 18:41:45.309451 | BON | GAJI 11 2010 | | 2800000 1500000 2010 10 22 2010 10 22 17:58:01.869139 | GAJI 11 2010 |
3000000
200000
1500000
7.4.2
PL/Python
Biasanya saya
Penulisan fungsi PostgreSQL bisa juga dalam bahasa Python. Pasanglah terlebih dahulu.
gunakan bahasa ini untuk algoritma yang tidak membutuhkan akses database.
1 2
$ $
sudo sudo
a p t g e t su
install
p o s t g r e s q l p l p y t h o n " createlang
8.4
totalindo "
postgres
plpythonu
Masih ingat modul uang yang ditaruh di /usr/local/lib/python2.6/dist-packages ? Kali ini akan kita gunakan pada fungsi di PostgreSQL. Buatlah uang.sql berikut ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
CREATE OR AS from
$$ ; $$ uang return
RETURNS t e x t
float
uang ( n )
CREATE OR AS from
$$ ; $$ uang return
RETURNS t e x t
integer
uang ( n )
Tidak seperti plpgsql, fungsi yang ditulis menggunakan plpythonu harus dibuat oleh user postgres.
BAB 7.
DATABASE
61
1 2 3 4
sudo
su
postgres for
" psql
totalindo
uang . s q l "
[ sudo ]
password
sugiana :
1 2 3 4 5
10.000 (1 row )
Bab 8
Python Akses Database
Data yang ada di PostgreSQL dapat digunakan oleh aplikasi yang ditulis dengan bahasa Python menggunakan pustaka tambahan bernama SQLAlchemy. Pustaka ini dipilih karena sifatnya yang luwes dimana ia juga bisa menangani database lainnya, tidak hanya PostgreSQL. Sekarang pasanglah terlebih dahulu.
1 2
$ $
sudo sudo
a p t g e t a p t g e t
install install
python s q l a l c h e m y python p s y c o p g 2
1 2 3 4 5
sqlalchemy
as
sa
Sampai di sini Python sudah terhubung ke PostgreSQL dan untuk melihat record tabel pegawai.
berhasil dengan perintah connect(). Anda bisa lanjutkan dengan perintah query
1 2 3 4 5 6 7 8 9
s q l = "SELECT for r in r q:
F R O M pegawai "
Putera ' ,
8, 2) ,
17) ,
True )
1) ,
19) ,
Lalu bagaimana kalau kita ingin melihat nilai berdasarkan nama eld-nya ? Untunglah variabel r di atas bisa dianggap sebagai dictionary yang keys-nya berisi nama eld dari query bersangkutan.
62
BAB 8.
63
1 2 3 4 5 6 7 8
> > > q = db . e x e c u t e ( s q l ) > > > ... ... 1 Bummi Dwi 2 3 4 Arief Cecep Nita Putera Setiadi Zahrudin Pandria for r in q: r [ ' id ' ] , r [ ' nama ' ] print
1 2 3 4 5 6 7 8
> > > q = db . e x e c u t e ( s q l ) > > > ... ... 1 Bummi Dwi 2 3 4 Arief Cecep Nita Putera Setiadi Zahrudin Pandria for r in q: r . id , r . nama print
Mudah bukan ? Fungsi execute() bisa juga digunakan untuk perintah query lainnya seperti INSERT, UPDATE, dan DELETE. Contohnya mengganti huruf pada eld nama dengan kapital.
1 2 3 4 5 6 7 8 9 10 11 12
> > > db . e x e c u t e ( s q l ) <s q l a l c h e m y . e n g i n e . b a s e . R e s u l t P r o x y > > > > > > ... ... BUMMI DWI PUTERA ARIEF SETIADI CECEP ZAHRUDIN NITA PANDRIA s q l = "SELECT for r in q: r . nama
F R O M pegawai "
8.1
Active Record
Anda memiliki data pegawai dalam sebuah le yang berformat CSV yang isinya seperti ini: Listing 8.1: pegawai.csv
1 2 3
ST; 1 9 8 5 0 8 1 7 ; t
BAB 8.
64
4 5
4 ; NITA PANDRIA; 1 9 7 6 0 9 1 9 ; f 5 ; ILHAM; 1 9 8 4 1 1 0 5 ; t File ini mencerminkan tabel pegawai dimana setiap eld dipisahkan dengan titik koma ( ; ). Sedangkan identitas baris berupa ID pegawai yang berada di kolom pertama, persis seperti tabel pegawai. Tugasnya adalah menyalin isi le ini ke dalam tabel pegawai. Jika ID pegawai sudah ada maka lakukan UPDATE, jika belum berarti INSERT. Mari kita lihat terlebih dahulu isi tabel pegawai melalui psql.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
psql
ilham for
localhost ilham :
totalindo
user
(8.4.4) connection " help " for ( cipher : help . DHE RSAAES256SHA, bits : 256)
+++ 1 | BUMMI DWI PUTERA | 1985 08 17 | t 2 | ARIEF SETIADI | 1972 05 02 | t 3 | CECEP ZAHRUDIN | 1972 06 01 | t 4 | NITA PANDRIA | 1976 09 19 | f
(4 rows ) Bandingkanlah dengan le pegawai.csv sebelumnya dimana perubahannya nanti adalah: 1. ID 1 nama akan berubah dari BUMMI DWI PUTERA menjadi BUMMI DWI PUTERA, ST (bergelar). 2. Jumlah baris tabel pegawai bertambah dengan adanya ID 5 bernama ILHAM. Buatlah le pegawai.csv terlebih dahulu dengan isi seperti di atas. Kemudian buatlah le updatepegawai.py.
1 2 3 4 5 6 7 8 9 10
import
url =
db = s a . c r e a t e _ e n g i n e ( u r l ) db . c o n n e c t ( ) filename = ' pegawai . csv ' f . readlines () : tgl_lahir , pria = line . strip () . s p l i t ( ' ; ' )
for
in
BAB 8.
65
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
F R O M pegawai W H E R E i d = %d " % p i d
q = db . e x e c u t e ( s q l )
if
q . rowcount : s q l = "UPDATE p e g a w a i SET " + \ "nama = '% s ' , " p r i a = %s " + \ " + \ " t g l _ l a h i r = '% s ' , " + \ tgl_lahir , pria , pid ) "W H E R E i d = %d "
else
s q l = s q l % ( nama , :
tgl_lahir ,
db . e x e c u t e ( s q l ) Jalankan.
python
u p d a t e p e g a w a i . py
1 2 3 4 5 6 7 8 9
+++ 1 | BUMMI DWI PUTERA, ST | 1985 08 17 | t 2 | ARIEF SETIADI | 1972 05 02 | t 3 | CECEP ZAHRUDIN | 1972 06 01 | t 4 | NITA PANDRIA | 1976 09 19 | f 5 | ILHAM | 1984 11 05 | t
(5 rows ) Perubahan sudah sesuai dengan yang diharapkan. Meski begitu source updatepegawai.py tergolong sulit dibaca, dan ini bisa menyulitkan debugging (penelusuran kesalahan). Cara yang lebih nyaman adalah menggunakan teknik yang disebut Terlebih dahulu pasang paket Elixir.
active record
a p t g e t
sudo
install
python e l i x i r
Agar proses UPDATE dan INSERT masih terasa, buatlah perubahan pada pegawai.csv berikut ini. Listing 8.3: pegawai.csv
1 2 3
ST; 1 9 8 5 0 8 1 7 ; t
BAB 8.
66
4 5 6
4 ; NITA PANDRIA; 1 9 7 6 0 9 1 9 ; f 5 ; ILHAM; 1 9 8 4 1 1 0 5 ; t 6 ;MIRANDA; 1 9 7 8 1 0 0 1 ; f Perubahannya adalah UPDATE pada CECEP ZAHRUDIN menjadi CECEP ZAHRUDIN, ST, dan INSERT pada MIRANDA. Selanjutnya buat le updatepegawai1.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
from
elixir
metadata ,
import
\
session
class
Pegawai ( E n t i t y ) :
' pegawai . csv ' f . readlines () : tgl_lahir , pria = line . strip () . s p l i t ( ' ; ' )
for
in
if else
python
u p d a t e p e g a w a i 1 . py
1 2 3 4 5 6
+++ 1 | BUMMI DWI PUTERA, ST | 1985 08 17 | t 2 | ARIEF SETIADI | 1972 05 02 | t 3 | CECEP ZAHRUDIN, ST | 1972 06 01 | t
BAB 8.
67
7 8 9 10
(6
4 5 6
| |
| | |
| | |
f t f
| MIRANDA rows )
Hasil sudah sesuai harapan dengan source yang jauh lebih mudah dibaca. Pahamilah baik-baik mengenai konsep active record ini. Lalu bagaimana menghapus salah satu record ? wai.py berikut ini. Buatlah le deletepega-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
from
elixir
setup_all , sys
import
\
session
Pegawai ( E n t i t y ) :
s y s . e x i t ( ' Caranya :
' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /
p = P e g a w a i . q u e r y . f i l t e r _ b y ( i d=p i d )
if not
s y s . e x i t ( ' ID
r = p . one ( ) r . delete ()
print
$ ID
Script ini membutuhkan parameter masukan (argument) saat dijalankan, yaitu berupa ID pegawai yang akan dihapus. Kita coba menghapus ID pegawai 4 NITA PANDRIA.
1 2
python
d e l e t e _ p e g a w a i . py 4 sudah dihapus
pegawai
BAB 8.
68
1 2 3 4 5 6 7 8 9
+++ 1 | BUMMI DWI PUTERA, ST | 1985 08 17 | t 2 | ARIEF SETIADI | 1972 05 02 | t 3 | CECEP ZAHRUDIN, ST | 1972 06 01 | t 5 | ILHAM | 1984 11 05 | t 6 | MIRANDA | 1978 10 01 | f
(5 rows ) Bagaimana dengan SELECT ? Contoh sebelumnya menggunakan lter_by() yang sama artinya dengan WHERE. Kini kita tampilkan semua record tabel pegawai tanpa kondisi apapun yaitu mengganti lter_by() dengan all(). Buatlah selectpegawai.py.
1 2 3 4 5 6 7 8 9 10 11
elixir sys
setup_all
import
Pegawai ( E n t i t y ) :
for
in print
r
Jalankan.
$ 1 2 3 5 6
python select_pegawai.py BUMMI DWI PUTERA, ST 1985-08-17 True ARIEF SETIADI 1972-05-02 True CECEP ZAHRUDIN, ST 1972-06-01 True ILHAM 1984-11-05 True MIRANDA 1978-10-01 False
Butuh tampilan yang urut berdasarkan nama ? Kita biasa menggunakan ORDER BY pada query, disini bisa menggunakan order_by() sebagai pengganti all().
1 2 3
from import
elixir sys
setup_all
import
BAB 8.
69
4 5 6 7 8 9 10 11
class
Pegawai ( E n t i t y ) :
for
$
in print
r
Jalankan.
1 2 3 4 5 6
python
2 ARIEF SETIADI 1972 05 02 1 BUMMI DWI PUTERA, 3 CECEP ZAHRUDIN, 5 ILHAM 1984 11 05
ST 1972 06 01
6 MIRANDA 1978 10 01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
elixir sys
import
Pegawai ( E n t i t y ) :
for
in print
r
Jalankan.
1 2 3 4
$ 2 1 3
python
BAB 8.
70
5 6
5 6
ILHAM MIRANDA
1984 11 05 1978 10 01
Pria Wanita
Elixir vs SQLAlchemy ?
Sebenarnya Elixir bukanlah menggantikan SQLAlchemy, karena Elixir sendiri menggunakan engine SQLAlchemy. python-elixir. Ini bisa terlihat pada penjelasan paket
1 2 3 4 5
dpkg
python e l i x i r python e l i x i r 0.7.1 1 p y t h o n (>= 2.4) , (>= python s u p p o r t 0.4.0) Mapper for SQLAlchemy (>= 0.90.0) ,
python s q l a l c h e m y Description :
Declarative
8.2
Auto Commit
Masih ingat fungsi sql bernama transaksi() ? Kali ini akan kita buat transaksi perbankan secara interaktif. Buatlah transaksi.py berikut.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import import
url =
db = s a . c r e a t e _ e n g i n e ( u r l )
p e g a w a i %s
tidak
q . rowcount : p e g a w a i %d tidak ada ' % p i d ) ' Nama : ' , q . f e t c h o n e ( ) . nama ') diisi ')
s y s . e x i t ( ' ID
harus
BAB 8.
71
24 25 26 27 28 29 30 31 32
try except
:
')
s y s . e x i t ( ' Nominal %s
s q l = "SELECT nominal )
q = db . e x e c u t e ( s q l )
' Transaksi
berhasil ,
ID ' ,
q . fetchone () . transaksi
Jalankan.
1 2 3 4 5 6 7
$ ID
python
t r a n s a k s i . py 3 ST GAJI 11 2010 ID 4
1 2 3 4 5 6 7
+++++ 1 | 1 | 2010 10 22 | GAJI 11 2010 | 3 0 0 0 0 0 0 | 3 0 0 0 0 0 0 2 | 1 | 2010 10 22 | BON | 200000 | 2 8 0 0 0 0 0 3 | 5 | 2010 10 22 | GAJI 11 2010 | 1 5 0 0 0 0 0 | 1 5 0 0 0 0 0
(3 rows ) Perhatikan, tidak ada ID transaksi 4, padahal eksekusi transaksi.py berjalan baik. Apa yang terjadi ? SQL function transaksi() yang dijalankan oleh transaksi.py dijalankan pada modus yang digunakan SQLAlchemy otomatis menjalankan BEGIN sebelum SELECT transaksi( ... ). Seperti kita ketahui BEGIN tanpa COMMIT sama artinya dengan ROLLBACK. Itu artinya semua aktitas DML seperti INSERT, UPDATE, DELETE setelah BEGIN dibatalkan, seolah tidak terjadi.
transaction
commit
1 2 3 4 5
Solusinya adalah memberitahukan SQLAlchemy untuk menjalankan . Ubahlah transaksi.py menjadi berikut ini. Listing 8.10: transaksi.py sqlalchemy sys as sa
auto
sqlalchemy . s q l . expression
import
text
BAB 8.
72
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
db = s a . c r e a t e _ e n g i n e ( u r l )
p e g a w a i %s
tidak
q . rowcount : p e g a w a i %d tidak ada ' % p i d ) ' Nama : ' , q . f e t c h o n e ( ) . nama ') diisi ') ')
s y s . e x i t ( ' ID
harus
s y s . e x i t ( ' Nominal %s
s q l = "SELECT nominal )
q = db . e x e c u t e ( t e x t ( s q l , ' Transaksi
berhasil ,
q . fetchone () . transaksi
q = db.execute(sql)
menjadi
q = db.execute(text(sql, autocommit=True))
8.3 Synchronizer
Pelanggan Anda memiliki usaha di bidang mini market dan telah memiliki beberapa outlet. Semua outlet itu telah terhubung ke Internet. Internet digunakan untuk mengambil data produk yang dikirim kantor pusat melalui email. Data
BAB 8.
73
produk tersebut tersimpan dalam attachment. User di outlet menyimpannya ke folder tertentu untuk selanjutnya dimasukkan ke database outlet melalui menu Restore yang telah disediakan di aplikasi. Begitu juga dengan data transaksi. Data disimpan terlebih dahulu ke dalam sebuah le melalui menu Backup. Selanjutnya le tersebut di-email ke kantor pusat. Teknik ini sudah baik, namun lebih baik lagi jika pekerjaan itu dilakukan oleh script yang disebut synchronizer. Kita membicarakan mekanisme otomatis penuh, dimana synchronizer dipicu oleh waktu, misalkan jam 9:00 dan jam 17:00 setiap harinya. Berbeda dengan teknik sebelumnya, synchronizer tidak menggunakan email untuk komunikasi data. Ia bekerja langsung dengan database pusat dan database outlet. Lalu bagaimana kedua host outlet dan pusat saling terhubung ? Ya, kita memerlukan outlet dapat mengakses database pusat. an baris berikut ini di
agar synchronizer di
/etc/postgresql/8.4/main/pg_hba.conf:
1 2
sudo
service
postgresql
8.4
restart server
Restarting
PostgreSQL
8.4
database
Kini saatnya uji konektivitas database. Misalkan IP publiknya adalah 110.137.120.251, maka jalankan
di outlet
h
: 110.137.120.251 totalindo
psql
ilham
url = 'postgresql://ilham:1234@110.137.120.251/totalindo'
Mudah bukan ?
8.3.1
Aspek Keamanan
Tapi nanti dulu. Meski outlet itu hanya mendapat akses terbatas di database pusat, namun tetap saja ini masih menimbulkan resiko keamanan data. Alangkah baiknya menggunakan teknik sebaliknya, yaitu
pusat
synchronizer berada di
Apa perlu pesan IP
outlet tidak mendapat hak akses apapun di database pusat. Lalu bagaimana host pusat mengakses host outlet ? publik lagi ke ISP yang ada di outlet ? Idealnya ya, namun tidak harus. Kita bisa menerapkan VPN alias Virtual Private Network. Cukup pasang VPN server di pusat, dan pasang VPN client di outlet. Dengan VPN ini terbentuk jaringan baru dengan IP pusat 10.8.0.1,
BAB 8.
74
sedangkan IP outlet 10.8.0.6, 10.8.0.10, 10.8.0.14, dst. Baik VPN server maupun VPN client bisa kita gunakan OpenVPN. Untuk pemasangan keduanya silahkan baca url berikut ini:
http://jabber.rab.co.id/os/konfigurasi-openvpn
Apakah OpenVPN dapat bekerja di Windows mengingat masih banyak outlet yang menggunakan sistem operasi ini ? Ya, untungnya OpenVPN tersedia di untuk sistem operasi itu, dan tetap dapat diatur agar otomatis aktif saat komputer dihidupkan. Baik di pusat maupun di outlet jalankan perintah ini untuk mengetahi IP VPN masing-masing:
1 2 3 4 5 6 7 8
ifconfig
tun0
00000000000000000000000000000000 :255.255.255.255 UP POINTOPOINT RUNNING NOARP MULTICAST :1500 frame : 0 TX p a c k e t s : 6 carrier :0 c o l l i s i o n s :0 RX b y t e s : 0 txqueuelen :100 B) TX b y t e s : 9 6 3 (963.0 B) (0.0 errors :0 dropped : 0 overruns :0 Metric : 1 errors :0 dropped : 0 overruns :0 RX p a c k e t s : 0
Tampak IP VPN di outlet adalah 10.8.0.6, sedangkan IP VPN di pusat biasanya 10.8.0.1. Di host pusat, Anda juga bisa lihat IP masing-masing client di le
/etc/openvpn/ipp.txt.
dengan perintah ping. OpenVPN telah terpasang dan kedua host pusat dan outlet sudah bisa saling ping. Selanjutnya apa yang harus dilakukan ?
di outlet
/etc/postgresql/8.4/main/pg_hba.conf
1 2
sudo
service
postgresql
8.4
restart server
Restarting
PostgreSQL
8.4
database
psql
ilham
10.8.0.6
totalindo
BAB 8.
75
8.3.2
1 2 3 4 5
create table
id serial nama name harga ) ;
1 2 3
Untuk menyamakan data produk, kita memerlukan dua perulangan (loop). Yang pertama untuk INSERT dan UPDATE, sedangkan yang kedua untuk DELETE. Berikut yang pertama: 1. Setiap
record pusat
dibaca.
2. Field id menjadi acuan, karena dia primary key. 3. Jika id tidak ditemukan di outlet maka tambahkan. 4. Jika id ditemukan maka perbaharui. Sedangkan untuk menghapus: 1. Setiap
record outlet
dibaca.
2. Jika id tidak ditemukan di pusat maka hapus yang ada di outlet. Lalu mulailah membuat sync.py berikut ini.
1 2 3 4 5 6 7
elixir
metadata
import
as
Entity ,
s q l a l c h e m y . orm
s q l a l c h e m y . schema
import import
ThreadLocalMetaData
BAB 8.
76
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
class class
u s i n g _ o p t i o n s ( t a b l e n a m e= ' p r o d u k ' ,
u s i n g _ o p t i o n s ( t a b l e n a m e= ' p r o d u k ' ,
setup_all ()
for
all_ps = ps
[]
p t = ProdukT . q u e r y . f i l t e r _ b y ( i d=p s . i d ) . o n e ( ) ' diubah ' e: ' ditambah ' s a . orm . e x c . NoResultFound ,
p t = ProdukT ( i d=p s . i d )
# DELETE
for
pt
if
in not in print
pt . i d pt . d e l e t e ( ) s t . commit ( )
BAB 8.
77
8.4
Anda sudah memiliki aplikasi yang menggunakan MySQL, dan kini ingin beralih ke PostgreSQL. Setidaknya dibutuhan penyalinan struktur tabel beserta isinya. Terlebih dahulu pasanglah driver MySQL.
sudo
a p t g e t
install
python mysqldb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
import
import import
sa
ThreadLocalMetaData sessionmaker
scoped_session ,
# source # target
for
tablename
class class
in
dbs . table_names ( ) :
u s i n g _ o p t i o n s ( t a b l e n a m e=t a b l e n a m e , a u t o l o a d=True ,
u s i n g _ o p t i o n s ( t a b l e n a m e=t a b l e n a m e ,
for
in
S o u r c e . mapper . c o l u m n s : column . t y p e ,
p r i m a r y _ k e y=column . primary_key ,
not
column . n u l l a b l e ,
BAB 8.
78
37 38 39 40
for
source
in
Source . query . a l l ( ) :
Contoh penggunaan:
python
d b m i g r a t i o n . py
m y s q l : / / r o o t : 6 7 8 9 @ l o c a l h o s t / sumber
postgres :// j e f r i :1234 @localhost / target Proses migrasi ini sudah menyertakan pembuatan tabel berikut constraint seperti PRIMARY KEY dan NULL. Untuk driver database lainnya Anda bisa lihat di informasi paket pythonsqlalchemy, dan perhatikan bagian Suggests.
1 2 3
$ ..
dpkg
Suggests :
python (>=
1)
python p y s q l i t e 1 . 1
2)
kinterbasdb
0.3)
5 ) , python python p y m s s q l
Bab 9
Lintas Sistem
Kita telah membahas mengenai manfaat modularitas dalam fungsi dan modul dimana sebuah aplikasi disusun bagaikan sebuah lego. Jika keseluruhan sistem semakin kompleks maka hindari konsep pemaksaan seperti semua harus ditulis dengan Python atau semua harus menggunakan PostgreSQL. Kali ini kita coba pahami bagaimana komunikasi antar sistem bisa terjadi tanpa pemaksaan platform tertentu.
9.1
Command Line
Script Python Anda membutuhkan informasi mengenai network device berikut IP-nya. Anda tidak tahu modul apa yang bisa digunakan. Namun Anda tahu betul bahwa command line ifcong sanggup melakukannya.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
ifconfig Link inet encap : Ethernet HWaddr 0 0 : 2 5 : b3 : 7 7 : 3 e : 9 9 addr : 1 9 2 . 1 6 8 . 1 1 . 1 1 1 Mask : 2 5 5 . 2 5 5 . 2 5 5 . 0 UP BROADCAST MULTICAST RX p a c k e t s : 0 frame : 0 TX p a c k e t s : 0 carrier :0 c o l l i s i o n s :0 RX b y t e s : 0 Interrupt :17 txqueuelen :1000 B) TX b y t e s : 0 (0.0 B) (0.0 errors :0 dropped : 0 overruns :0 errors :0 M T U: 1 5 0 0 dropped : 0 Metric : 1 overruns :0 Bcast : 1 9 2 . 1 6 8 . 1 1 . 2 5 5
eth0
lo
encap : L o c a l addr :
addr : 1 2 7 . 0 . 0 . 1 ::1/128
UP LOOPBACK RUNNING
79
BAB 9.
LINTAS SISTEM
80
15 16 17 18 19 20 21 22 23 24 25 26 27
wlan0
errors :0 errors :0
dropped : 0 dropped : 0
overruns :0 overruns :0
txqueuelen :0 ( 5 4 0 . 8 KB) TX b y t e s : 5 4 0 8 4 8
RX b y t e s : 5 4 0 8 4 8
HWaddr
0 0 : 1 f : 3 c : e0 : 6 6 : 5 6
Bcast : 1 9 2 . 1 6 8 . 1 . 2 5 5 Scope :
Mask : 2 5 5 . 2 5 5 . 2 5 5 . 0 fe80 : : 2 1 f :3 c f f : fee0 :6656/64 M T U: 1 5 0 0 overruns :0 overruns :0 Link UP BROADCAST RUNNING MULTICAST Metric : 1 RX p a c k e t s : 3 2 1 9 frame : 0 TX p a c k e t s : 3 6 0 8 carrier :0 c o l l i s i o n s :0 ( 7 6 1 . 1 KB) Bagaimana memanggil command line di script Python dan mengolah hasilnya ? Gunakan modul commands. Buatlah netdev.py berikut ini. txqueuelen :1000 (1.5 M B) TX b y t e s : 7 6 1 1 7 3 RX b y t e s : 1 5 8 6 2 9 7 errors :0 dropped : 0 errors :0 dropped : 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
in
=
match = r e . c o m p i l e ( ' ( . * ) L i n k
if
match = r e . c o m p i l e ( ' i n e t
if continue
BAB 9.
LINTAS SISTEM
81
17 18 19 20 21 22 23 24 25
if return if for
dev
__name__ ==
'__main__ ' : nd :
nd = n e t d e v ( )
in print
dev ,
nd [ d e v ]
Jalankan.
1 2 3 4
$ lo
n e t d e v . py '192.168.1.4 ' , ' ad dr ' : ' 0 0 : 1 f : 3 c : e0 : 6 6 : 5 6 ' } ' 0 0 : 2 5 : b3 : 7 7 : 3 e '127.0.0.1 '} '192.168.11.111 ' , ' addr ' :
wlan0 eth0
Lintas sistem yang menggunakan command line ini tergolong lokal, artinya netdev.py harus berada di komputer yang sama dengan ifcong.
9.2
File
File bisa digunakan sebagai sarana pemersatu input dan output. Misalkan Anda sudah membuat sebuah daemon yang dapat menerima dan mengirim SMS. Daemon ini pengendali modem. Oh ya, daemon adalah program yang selalu hidup untuk siap menerima perintah. Di sisi lain Anda juga punya program penerjemah SMS yang masuk (SMS parser) dan juga berfungsi untuk membalas ke si pengirim. Bagaimana kedua program ini dapat berhubungan ? Anda bisa menggunakan le sebagai perantara. Saat ada SMS masuk, daemon pengendali modem menyimpannya dalam sebuah le di direktori /var/spool/modem/inbox. Kemudian SMS parser membaca direktori ini, menerjemahkan lenya, dan membalas SMS tersebut dengan cara membuat le di direktori /var/spool/modem/outbox. File tersebut dibaca oleh daemon si modem untuk kemudian mengirim SMS yang dimaksud. Baik daemon modem maupun SMS parser bisa ditulis dengan bahasa yang berbeda. Mungkin yang pertama dengan C++, sementara yang satunya di tulis dengan Python. Namun keduanya bekerja menggunakan direktori yang sama. Ini juga tergolong lintas sistem yang lokal, keduanya harus di komputer yang sama. Dengan teknik NFS atau Samba Anda bisa membuat direktori bersama (sharing directory) di komputer lain (remote host).
BAB 9.
LINTAS SISTEM
82
9.3
Database
Pelanggan Anda telah memiliki sebuah sistem pengisian pulsa dimana terdapat fungsi yang berkaitan dengan GSM modem. Fungsi yang dimaksud adalah yang berkaitan dengan SMS gateway yaitu mengirim dan menerima SMS. Pelanggan ini telah membuat sistemnya menggunakan Borland Delphi (bahasa Pascal) dan menggunakan database MySQL. Ia mengeluh pada Anda selaku developer outsourcing dimana komponen SMS gateway yang dibangunnya tidak stabil. Di sisi lain Anda telah membuat SMS gateway yang ditulis dengan bahasa Python dan database PostgreSQL. Tidak seperti yang dimiliki pelanggan tersebut, SMS gateway yang Anda buat sangat stabil dan telah teruji bertahun-tahun. Masalah penyatuan semakin bertambah dimana SMS gateway Anda berada di sistem operasi Linux, sementara pelanggan Anda menggunakan Microsoft Windows. Langkah apa yang diambil untuk membuat SMS gateway-nya stabil ? Anda belajar Borland Delphi untuk menerjemahkan algoritma Python ? pengisian pulsanya ? Langkah yang bijaksana tentu tidak keduanya, karena porsi tidak seimbang yang mengakibatkan penyatuan menjadi sangat lama. Solusi sederhana adalah menggunakan database sebagai perantara. Mintalah padanya untuk menyediakan sebuah komputer lagi untuk SMS gateway Anda, sebut saja server SMS gateway. Server ini terhubung melalui LAN (local area network) dengan server pengisian pulsa yang berbasis Windows tadi. Tanpa perubahan ? Tentu saja ada. Rancangan SMS gateway yang Anda buat harus dapat mengirim SMS hanya dengan menggunakan perintah INSERT. Contoh: im . a n t r i a n ( p e n e r i m a , ' Selamat pesan ) Atau memaksa pelanggan Anda itu untuk belajar Python dan membangun ulang sistem
INSERT INTO
+628177654321 ' ,
bergabung ' ;
SELECT
'
source Borland Delphi-nya, yaitu pada fungsi pengiriman SMS menggunakan koneksi ke database PostgreSQL yang ada di server SMS gateway. driver ODBC dapat mengatasi hal ini. Lho, bukankah ia menggunakan MySQL sebagai database ? Benar, MySQL untuk komponen pengisian pulsa tetap berlaku. Jadi dalam aplikasi Borland Delphi terdapat dua koneksi database, yaitu ke MySQL yang ada di localhost Microsoft Windows, dan ke PostgreSQL yang ada di remote host Linux. Lalu bagaimana untuk membaca SMS yang masuk ? Kembali, Anda diminta untuk menyimpan SMS yang masuk ke dalam sebuah tabel, katakanlah bernama inbox. Selanjutnya pelanggan Anda dapat membacanya menggunakan perintah SELECT pada Borland Delphi.
SELECT
F R O M im . a n t r i a n W H E R E NOT k i r i m ;
Mudah bukan ? Lebih lanjut mengenai SMS gateway bisa lihat halaman114.
BAB 9.
LINTAS SISTEM
83
Lintas sistem ini tergolong bisa beda komputer, karena baik PostgreSQL maupun MySQL dapat diakses melalui jalur network.
9.4
XMLRPC
Salah
satunya adalah aspek tanggung jawab, atau biasa disebut aspek keamanan (security). Misalkan dalam sistem perbankan. Sistem utama sebuah bank dibangun oleh PT Software Aman Banget (SAB). Namanya juga sistem utama, ia memuat data penting nasabah serta data yang menyangkut sistem. Suatu saat bank ini ingin go Internet dimana nasabah dapat melakukan transaksi melalui browser Firefox. Berarti dibutuhkan web developer dan dipilihlah PT Web Perkasa (WP) untuk mengembangkannya. Karena mengembangkan Internet banking, berarti ada proses login untuk nasabah. Ini berarti ada algoritma pemeriksaan username dan password. Sampai disini tidaklah menjadi masalah karena username dan password web bisa berada di web server yang dibangun oleh WP. Tibalah saatnya nasabah ingin melihat mutasi transaksinya. SAB ? Mayoritas jawabannya tidak. Mengapa ? SELECT berarti kita bicara koneksi ke database. Itu artinya WP mengetahui username dan password untuk login ke database sistem utama. Kalau sudah begitu maka WP bisa melihat-lihat data nasabah lainnya, bahkan melihat tabel-tabel yang menyangkut sistem. Jelas ini sudah terlalu jauh dan bisa menimbulkan hal-hal yang tak perlu. Untuk menghindari koneksi database ke sistem utama, maka digunakanlah XMLRPC sebagai perantaranya. Makanan apakah ini ? XMLRPC adalah XML Remote Procedure Call, yaitu sebuah bahasa pertukaran data yang ditulis dalam format XML. Kita dapat memandang RPC sebagai pemanggilan fungsi yang memiliki nilai masukan (input parameter) dan nilai keluaran (output / return value). Sebagaimana fungsi sederhana ini. Apakah WP melakukan SELECT tabel ke sistem utama bank tersebut yang dibangun oleh
1 2
def
tambah ( a , a + b
b) :
return
sebut dikirim menggunakan HTTP atau HTTPS dengan method POST. Karena web service, maka kita bicara koneksi client - server, sehingga ada penyebutan XMLRPC client dan XMLRPC server. Penggunaannya tergolong simple dan pustakanya (library) banyak tersedia untuk berbagai bahasa seperti Python, PHP, Java, Borland Delphi, Visual Basic, dst. Kembali pada web developer di atas. WP mengembangkan web-nya menggunakan PHP, sementara SAB mengembangkan sistem bank menggunakan Python. Database keduanya juga berbeda dimana WP menggunakan MySQL, sedangkan SAB menggunakan PostgreSQL. Namun semuanya itu tidak lagi menjadi hal.
web service
BAB 9.
LINTAS SISTEM
84
Langkah apa yang harus dikembangkan terlebih dahulu ? Jelas bahwa SAB harus mengembangkan XMLRPC server terlebih dahulu yang berisi fungsi mutasi(). Mari kita mulai membuat le serverbank.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
SimpleXMLRPCServer sqlalchemy as sa
import
SimpleXMLRPCServer
S e r v e r ( SimpleXMLRPCServer ) :
def
print return
in
def
return
r e s p ( code ,
class def
Agent :
def
_dispatch ( s e l f , method ,
method ,
params =() ) :
func = g e t a t t r ( s e l f , AttributeError : r e s p ( 1 ,
' F u n g s i %s
method )
export_mutasi ( s e l f , s q l = "SELECT
t g l : : date ,
"F R O M transaksi W H E R E p i d = %d "ORDER BY i d " s q l = s q l % (p [ ' pid ' ] , q = db . e x e c u t e ( s q l ) m = p [ ' awal ' ] ,
" + \ " + \
for
[] r
m. append ( [ s t r ( r . t g l ) , saldo ] )
in
q: r . ket , r . nominal , r.
BAB 9.
LINTAS SISTEM
85
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
url =
r = r e s p ( 0 , 'OK ' )
return
= m
print
$
1 2
python
s e r v e r b a n k . py port 9303
HTTP XMLRPC s e r v e r
Proses seolah hang, tapi sebenarnya ia sedang menunggu permintaan (request) dari XMLRPC client. Port 9303 hanya contoh saja dimana Anda bisa mengganti dengan nilai lainnya. Security yang diterapkan di sini adalah IP client yang hanya menerima permintaan dari 127.0.0.1 dan 192.168.0.1. Selanjutnya membuat XMLRPC client bernama clientbank.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import
url =
Listing 9.3: clientbank.py xmlrpclib ' http : / / 1 2 7 . 0 . 0 . 1 : 9 3 0 3 ' 1, ' 20101022 ' , ' 20101023 ' }
remote = x m l r p c l i b . ServerProxy ( u r l ) d = { ' pid ' : ' awal ' : ' akhir ' :
r = remote . mutasi ( d )
if
r [ ' k o d e ' ] == 0 :
for
:
tgl ,
ket , tgl ,
nominal ,
saldo
ket . l j u s t (15) ,
in
else
$
1 2 3
python
200000
BAB 9.
LINTAS SISTEM
86
Cermati baik-baik konsepnya. Juga perhatikan tipe data dictionary dan list yang digunakan sebagai nilai keluaran (return value) dari fungsi mutasi.
9.4.1
Unduh xmlrpc.inc dari http://phpxmlrpc.sourceforge.net. xmlrpc-2.2.2.tar.gz. Carilah. buatlah le clientbank.php. Listing 9.4: clientbank.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
<?php
$params = new ( ' pid ' ' awal ' ) , ' struct ' ) ; $ f = new $ c = new
include array
( ' xmlrpc . i n c ' ) ; xmlrpcval ( = > new = > new xmlrpcval (1 , ' i n t ' ) , xmlrpcval ( ' 20101022 ' ) , xmlrpcval ( ' 20101023 ' )
array
( $params ) ) ; 9303) ;
$ r = $c >s e n d ( $ f ) ; $v = $ r >v a l u e ( ) ;
if
( $r >f a u l t C o d e ( ) )
$r >f a u l t S t r i n g ( ) ;
$ r e s p = $v > s c a l a r v a l ( ) ; ( $ r e s p [ ' k o d e ' ]> s c a l a r v a l ( ) == 0 ) $ f = $m > s c a l a r v a l ( ) ; = $ f [0] > s c a l a r v a l ( ) ; = $ f [1] > s c a l a r v a l ( ) ; = $ f [3] > s c a l a r v a l ( ) ; ". ". ". ( $ket , 2 0 ) . " { as $ i= >$m) { ( $ r e s p [ ' m u t a s i ' ]> s c a l a r v a l ( )
$ n o m i n a l = $ f [2] > s c a l a r v a l ( ) ;
} } } } ?>
$tgl . "
( $nominal , 1 0 ) . "
( $ s a l d o , 1 0 ) . " \n" ;
BAB 9.
LINTAS SISTEM
87
Karena script ini akan kita jalankan sebagai command line, maka pasang dulu paket terkait.
sudo
a p t g e t
install
php5 c l i
Lalu jalankan.
1 2 3
php
200000
9.4.2
Drupal
Drupal
1 juga ditulis dengan PHP. Ia sudah memuat function xmlrpc, sehingga
Cara penulisannya juga lebih
Anda tidak perlu memasang modul tambahan. mudah. Contoh berikut ini untuk Drupal 6.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
$ i t e m s [ ' p e g a w a i / t r x/%/%/% ' ] ' title ' = > ' page ' page ' Transaksi ' , callback ' = > arguments ' = >
array
{ =
() ;
array
array
(2 ,3 ,4) ,
pegawai_trx ( $pid ,
$awal ,
$akhir )
$url =
array
' http : / / 1 2 7 . 0 . 0 . 1 : 9 3 0 3 ' ; ( ' pid ' = > ' awal ' = > $awal , ' akhir ' = > $akhir ) ; ' mutasi ' , { ada data ' ) ; $p ) ;
intval
( $pid ) ,
$r = xmlrpc ( $ u r l ,
if
} $c = '<t a b l e > <th>T a n g g a l </th> <th>K e t e r a n g a n </th> ' . '<th>Nominal </th> <th>S a l d o </th ></t r > ' ; ( $r [ ' mutasi ' ] as $ i= >$ f ) {
foreach
$tgl = $f [ 0 ] ;
1 http://drupal.org
BAB 9.
LINTAS SISTEM
88
27 28 29 30 31 32 33 34 35 36 37
} }
$ket = $f [ 1 ] ; $nominal = $ f [ 2 ] ; $saldo = $f [ 3 ] ; $c $c $c $c $c return .= .= .= .= .= $c ; '<t r > <td> ' . $ t g l . ' </td> ' ; '<td> ' . $ k e t . ' </td> ' ; '<t d '<t d a l i g n =" r i g h t "> ' . format_number ( $ n o m i n a l a l i g n =" r i g h t "> ' . format_number ( $ s a l d o ) .
) . ' </td> ' ; ' </td> ' ; ' </ t r > ' ;
1 2 3 4
name = P e g a w a i d e s c r i p t i o n = Data core = 6. x v e r s i o n = " 6 . x 1.0" Setelah login di browser, masuklah ke menu admin/build/modules. Centang modul pegawai dan klik Save. Lalu masukkan URL di browser seperti pegawai
pegawai/trx/1/20101022/20101023
Bab 10
Pengemasan
10.1
Paket Debian
Pada bab sebelumnya kita sudah membuat modul uang.py. Agar modul ini dapat digunakan oleh user lainnya maka diletakkanlah di direktori /usr/local/lib/python2.6/distpackages. Proses copy modul seperti ini memang sudah mudah, tapi ada lagi yang lebih memudahkan, yaitu dengan mengemasnya dalam bentuk paket Debian. Langkah kemudahan yang dimaksud adalah: 1. Tambahkan URL daftar paket dalam sebuah le /etc/apt/sources.list.d/custom.list yang berisi deb http://192.168.0.1/deb ./ 2. Perbaharui daftar paket $ sudo apt-get update 3. Pasang paket dimaksud $ sudo apt-get install python-uang Manfaat lain pemaketan adalah mudahnya pemasangan paket lainnya yang dibutuhkan oleh paket utama, sering disebut sebagai paket python-uang tadi membutuhkan python-sqlalchemy, maka pada saat
dependencies
. Misalkan
sudo
a p t g e t
install
python uang
otomatis python-sqlalchemy juga ikut dipasang. Penamaan python-uang dimana ada awalan python- dikarenakan uang.py adalah modul Python yang umum. sa digunakan di aplikasi apa saja. Dengan kata lain biTidak ada koneksi database di
sana. Juga tidak ada perintah akses database seperti SELECT dkk. Awalan ini juga meniru dari paket umum lainnya seperti pythonsqlalchemy, python-psycopg2, python-serial, dst.
89
BAB 10.
PENGEMASAN
90
sudo
mkdir
Perintah ini otomatis membuat dua buah direktori yaitu 1. /usr/local/src/python-uang 2. /usr/local/src/python-uang/DEBIAN Direktori DEBIAN digunakan untuk meletakkan le-le yang berkaitan dengan sistem paket seperti
berisi informasi seputar paket. script yang dijalankan saat pemasangan berlangsung, yaitu saat apt-
get install atau dpkg -i. Script ini harus executable yang bisa diset dengan chmod 755. script yang dijalankan saat penghapusan berlangsung, yaitu saat apt-
conles
get remove atau dpkg -r. Script ini juga harus executable. berisi daftar nama le kongurasi yang digunakan paket ini. MisalJadi tidak hard-code menggunakan loca-
nya berisi /etc/uang.conf. File kongurasi ini berisi format negara yang digunakan oleh fungsi uang(). le id_ID.UTF-8. Dengan begitu python-uang semakin umum (general). Dengan didaftarkannya /etc/uang.conf di dalam conles, maka terhindar dari penibanan (timpa / overwrite) saat upgrade berlangsung. an kini python-uang versi 0.1. memuat /etc/uang.conf. Jika Misalk-
apa-apa pada /etc/uang.conf maka apt-get install tidak akan melakukan timpa pada /etc/uang.conf yang . Karena siapa tahu Anda sudah mengganti isi /etc/uang.conf terpasang dengan de_DE.ISO8859-1. Dengan begitu kita terhindar dari mengubah ulang kongurasi. Mudah-mudahan paham maksudnya. Mulailah membuat le python-uang/DEBIAN/control. Listing 10.1: DEBIAN/control
Kemudian dibuatlah versi 0.2 yang juga versi 0.2 tidak ada perubahan
1 2 3 4 5 6 7 8 9
python uang optional python Owo S u g i a n a <s u g i a n a @ r a b . c o . i d > all (>= 0.6.7)
Architecture :
Python V e r s i o n : Description :
BAB 10.
PENGEMASAN
91
1 2
$ $
cd
sudo
uang / s i t e
tori tersebut nantinya akan dipasang di root directory ( / ), kecuali direktori Lalu mengapa menggunakan /usr/share ? Padahal sebelumnya kita meletakkan uang.py di /usr/local ? Direktori /usr/local yang kita gunakan sebelumnya bermakna bahwa uang.py belum menjadi bagian dari paket Debian. Bila sudah menjadi bagian dari paket Debian, maka diletakkan di /usr/share. Begitulah ketentuan umumnya.
sudo
cp
/ u s r / l o c a l / l i b / p y t h o n 2 . 6 / d i s t p a c k a g e s / uang . py
Karena akan menggunakan /etc/user.conf, lakukan perubahan pada uang.py menjadi berikut ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
types
ConfigParser
import
import
IntType ConfigParser
conf = ConfigParser () c o n f . r e a d ( ' / e t c / uang . c o n f ' ) l o c a l e . s e t l o c a l e ( l o c a l e . LC_ALL, locale ')) DECIMAL = c o n f . g e t i n t ( ' d e f a u l t ' , ' d e c i m a l ' ) conf . get ( ' default ' , '
def if
is
p e c a h a n=None ) : None :
pecahan = 0 p e c a h a n = DECIMAL
is
p e c a h a n=None ,
t a n d a=None ) :
'%s%s ' % ( t a n d a ,
BAB 10.
PENGEMASAN
92
26 27 28 29 30 31
if
__name__ == n = 10000.5
'__main__ ' :
sudo
mkdir
python uang / e t c
1 2 3
[ default ] locale = id_ID . UTF 8 decimal = 2 Lalu buat python-uang/DEBIAN/conles berisi satu baris berikut. Listing 10.4: DEBIAN/conles
/ e t c / uang . c o n f Kemudian buat python-uang/DEBIAN/postinst yang berisi perintah untuk mendaftarkan uang.py agar dikenal sebagai modul. Listing 10.5: DEBIAN/postinst
1 2
1 2
1 2
$ $
sudo sudo
chmod chmod
755 755
sudo
chown
root . root
python uang
1 2 3 4 5
find
python uang
python uang / python uang /DEBIAN python uang /DEBIAN/ c o n t r o l python uang /DEBIAN/ prerm
BAB 10.
PENGEMASAN
93
6 7 8 9 10 11 12 13 14 15
python uang /DEBIAN/ c o n f f i l e s python uang /DEBIAN/ p o s t i n s t python uang / u s r python uang / u s r / s h a r e python uang / u s r / s h a r e / p y c e n t r a l python uang / u s r / s h a r e / p y c e n t r a l / python uang python uang / u s r / s h a r e / p y c e n t r a l / python uang / s i t e python uang / u s r / s h a r e / p y c e n t r a l / python uang / s i t e / uang . py python uang / e t c python uang / e t c / uang . c o n f Kemas.
p a c k a g e s p a c k a g e s
1 2 3 4
sudo
dpkgdeb
b u i l d
peringatan : membuat
' python uang /DEBIAN/ c o n t r o l ' ' PythonV e r s i o n ' di dalam about ` . / python the ` python uang ' 1
contains
u s e r d e f i n e d paket
Perhatikan ada titik diakhirnya yang berarti letakkan di current directory. Abaikan saja peringatan itu. Karena Python-Version memang bukan standar Debian, tapi digunakan oleh pycentral. Sampai di sini pembuatan paket selesai. Kini telah terbentuk python-uang_0.1_all.deb yang siap dipasang.
1 2 3 4 5 6
sudo
dpkg
python uang_0 . 1 _ a l l . deb python uang basis paket data yang sebelumnya tidak dan dipilih . direktori ...213458 berkas
Memilih ( Sedang
paket
membaca
telah
terpasang . ) python uang (0.1) ( dari ... ... python uang_0 . 1 ... python uang for python c e n t r a l
triggers
Untuk menghindari kerancuan karena ada dua modul uang yang terpasang, sebaiknya buang yang ada di /usr/local.
1 2 3 4 5
python 2.6.5 ( r265 :79063 , on linux2 " credits " or "license" for more " copyright " , import uang Apr 16 2010 , 13:09:56) 4.4.3] " help " , uang
BAB 10.
PENGEMASAN
94
6 7
> > > uang ( 1 0 0 0 0 ) '10.000 ' Ujian terakhir hapuslah paket ini untuk memastikan script DEBIAN/prerm berfungsi dengan baik.
1 2 3 4 5 6 7 8 9 10 11
sudo
a p t g e t membaca
ketergantungan tersedia . . .
informasi
python uang dimutakhirkan , tidak Setelah Anda akan operasi dimutakhirkan . ini , 0B r u a n g [ Y/ t ] ? data ...213460 ... berkas dan direktori kosong harddisk
digunakan . ingin telah Sedang melanjutkan basis terpasang . ) python uang ( Sedang membaca membuang
10.2
Debian Repository
Kini saatnya membuat repository agar bisa di-apt-get dari komputer lain. Pasanglah paket yang dibutuhkan untuk membaca le-le *.deb.
sudo
a p t g e t
install
dpkgd e v
1 2
$ $
sudo sudo
mkdir cp
/ deb Lalu buatlah /var/www/deb/update-list.sh. File ini digunakan untuk memperbaharui daftar paket yang tersedia. Listing 10.7: updatelist.sh
1 2 3
rm
f Packages . gz dpkg s c a n p a c k a g e s a r c h g z i p b e s t P a c k a g e s
all
/ dev / n u l l > P a c k a g e s
1 2
$ $
cd
sudo
Akan terbentuk le Packages.gz. File inilah yang akan dibaca saat apt-get update. Selanjutnya kita uji repository ini dengan membuat le /etc/apt/sources.list.d/custom.list.
BAB 10.
PENGEMASAN
95
deb
h t t p : / / 1 9 2 . 1 6 8 . 0 . 1 / deb
./
sudo
a p t g e t
update
sudo
a p t g e t
install
python uang
10.3
Remastering Ubuntu
Untuk semakin memudahkan pemasangan, ada baiknya pasang paket-paket yang dimaksud ke dalam sebuah le ISO. Bukan hanya memasang, bahkan Anda bisa mengkongurasi secara manual untuk memastikan pemasangan Linux Ubuntu dan aplikasi yang dimaksud berlangsung lancar dan Mulailah pasang paket yang dibutuhkan.
mudah
sudo
a p t g e t
install
s q u a s h f s t o o l s
mkisofs
1 2 3
SOURCE =" /home/ s u g i a n a / Unduhan / b l a n k o n 6.0 c d l i v e i 3 8 6 . i s o " TARGET =" b l a n k o n 6.0 c d l i v e s o h o i 3 8 6 . i s o " LABEL =" BlankOn 6 SOHO"
1 2 3 4 5 6 7 8
./ conf
'
/tmp/ cdrom /
cdrom /
t
.
squashfs
/tmp/ cdrom / c a s p e r / f i l e s y s t e m .
squashfs
/tmp/ r o o t
/tmp/ r o o t /tmp/ r o o t
umount umount
/tmp/ cdrom
1 2 3
cp
chroot chroot
t t
BAB 10.
PENGEMASAN
96
4 5 6 7 8 9 10 11 12 13
rm
root / etc / resolv . conf clean sys proc /tmp / * / root / . bash_history
a p t g e t umount umount rm
r f
r f
W s h o w f o r m a t = ' $ { P a c k a g e }
cdrom / c a s p e r /
${
sed
desktop
1 2 3 4 5 6 7
. rm
./ conf
awal =` date `
cd
mkisofs
8 9 10 11
cat
l l
$SOURCE
. . / $TARGET
awk awk
|
'{ p r i n t
$5 } ' ` $5 } ' `
'{ p r i n t
sudo
s h 1 e x t r a c t . s h Kemudian jalankan
Dia akan mengekstrak le iso menjadi root directory. script kedua:
sudo
s h 2 c h r o o t . s h
Script ini untuk masuk ke sistem Linux yang lain, yaitu root directory tadi.
1 2 3 4
cd
wget
a p t g e t a p t g e t vim
squid
BAB 10.
PENGEMASAN
97
Kalau sudah selesai keluarlah dari chroot dengan menekan Ctrl-D atau ketik exit:
1 2 3
r o o t @ l a p t o p :/# exit
exit
sugiana@laptop :~ $ Root directory kini telah berisi aplikasi yang dibutuhkan. Saatnya membuat iso dengan menjalankan script ketiga.
1 2 3
Parallel Creating
Using on
processors
filesystem size
squashfs ,
131072.
[ = = = = = = = = = = = = = = = = = = = = = = = = = = |
Terakhir script akan memberikan informasi ukuran iso yang dihasilkan berikut iso aslinya.
1 2 3 4 5
written 2
(698 M B)
0 9 : 4 5 : 3 9 WIT 2 0 1 0 0 9 : 5 0 : 5 2 WIT 2 0 1 0
731869184 732022784
Jika media yang Anda tuju nanti adalah CDROM, pastikan ukurannya dibawah 700MB. Lebih mudahnya bandingkan dengan ukuran ISO aslinya. Pada contoh di atas terlihat ukuran Target masih lebih besar dari Source. Jika masih terlalu besar jalankan lagi script kedua untuk menghapus paket yang tak perlu.
sudo
s h 2 c h r o o t . s h
sudo
s h 3 p a c k . s h
File ISO yang dihasilkan bisa Anda coba terlebih dahulu di VirtualBox . Anda juga bisa memasangnya di ashdisk menggunakan usb-creator-gtk. Jika sudah yakin barulah membakarnya (burn) ke CD-ROM.
1 virtualbox.org
Bab 11
Graphical User Interface
Bab-bab sebelumnya kita mempelajari bagaimana membuat aplikasi dalam lingkungan teks (text mode), beberapa menyebutnya lingkungan konsole. Contohnya fungsi raw_input() yang meminta masukan user dari konsole. Lingkungan yang dimaksud bercirikan tidak membutuhkan mouse, cukup dengan keyboard saja sebagai alat masukan (input device). Kali ini kita masuk pemrograman berbasis gras dimana mouse bisa digunakan sebagai alat masukan untuk klik tombol, copy paste, dsb. Graphical Uset Interface (GUI) tidaklah wajib di Python, ia hanya sebagai modul. Berbeda dengan Visual Basic atau Borland Delphi yang mewajibkan GUI. Modul berbasis GUI tidak hanya satu di Python, ada Qt, Wx, GTk, Tcl, dst. Di sini kita akan membahas Qt sebagai pustaka antarmuka gras. Pasanglah paketnya.
sudo
a p t g e t
install
python q t 4
sudo
a p t g e t
install
q t 4 doc h t m l
Seperti biasa, mulailah dari yang sederhana, yaitu membuat aplikasi Hello world dalam le hello.py.
1 2 3 4 5 6 7 8 9 10
PyQt4
PyQt4 . QtGui
import
import
Qt
98
BAB 11.
99
11 12 13 14
fm.show()
menjadi
fm.showMaximized()
11.1 Orientasi Objek
Di sini teknik pemrograman semakin terstruktur dimana pemrograman berorientasi objek (Object Oriented Programming / OOP) diterapkan. rinci lagi pembahasan source hello.py tersebut. Class bisa dianggap sebuah tipe data, disejajarkan dengan tipe string, integer, oat, list, dictionary, datetime, dst. Dengan demikian FormUtama merupakan tipe data. Perhatikan baris 5 Mari kita
class FormUtama(QMainWindow):
itu artinya FormUtama merupakan turunan (inheritance) dari class QMainWindow. Itu artinya variabel dan fungsi yang dimiliki class QMainWindow juga dimiliki oleh class FormUtama. Bahasa lainnya FormUtama mewarisi sifat QMainWindow. Selanjutnya ada fungsi __init__(). pembentukan ? Ya, perhatikan baris 12 Fungsi ini merupakan fungsi khusus yang dijalankan saat pembentukan sehingga disebut sebagai constructor. Saat
fm = FormUtama()
Itu adalah pembentukan variabel fm yang bertipe FormUtama. Disinilah fungsi __init__() dipanggil.
BAB 11.
100
Dalam istilah OOP, fm disebut sebagai instance atau variabel bertipe class. Namun di Python rasanya semua tipe data adalah class. Misalnya tipe string. Ia tidak hanya memuat data seperti 'abcd' saja, melainkan juga memuat fungsi seperti replace(), rjust(), dst. Hal ini bisa dibuktikan dengan print dir('abcd'). Sehingga boleh dikatakan di Python semua variabel adalah objek. Bagi programmer PHP Anda bisa cermati bahwa string 'abcd' hanya sekedar data. Sekarang perhatikan baris 7
QMainWindow.__init__(self)
Fungsi __init__() yang dimiliki QMainWindow perlu dipanggil lagi. Mengapa ? Karena FormUtama mendenisikan ulang fungsi __init__(). Lalu apa itu self ? self adalah variabel yang mewakili FormUtama itu sendiri. Setiap fungsi yang dimiliki oleh suatu class setidaknya memiliki satu masukan (input parameter) yaitu self. Selanjutnya lihat baris 13
fm.show()
dimana kita tidak pernah mendenisikan fungsi show() di dalam class FormUtama. Kenyataannya variabel fm memiliki fungsi show(). Darimana ia berasal ? Seperti dijelaskan tadi, FormUtama mewarisi sifat QMainWindow, jadi fungsi show() itu berasal darinya. Fungsi ini digunakan untuk menampilkan form. Lalu apa itu form ? Kotak Hello world itulah yang disebut form seperti pada gambar 11.1. Sekarang lihat baris 11
app = Qt.QApplication(sys.argv)
dimana QApplication dibutuhkan untuk loop. Loop yang dimaksud adalah baris terakhir
app.exec_()
tanpa loop, alur langsung berakhir. Form bisa banyak, tapi QApplication cukup satu saja. Pahami lagi baik-baik konsep OOP ini agar semakin mudah untuk membuat aplikasi yang lebih besar.
11.2
Daftar Pegawai
Form ini untuk memasukkan data
pegawai, terinspirasi dari tabel pegawai yang pernah kita buat. Namun disini belum menggunakan database, semuanya tersimpan sementara saja.
BAB 11.
101
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
PyQt4
PyQt4 . QtGui
import
PyQt4 . QtCore
import * import
Qt
QDate ,
SIGNAL
FormUtama ( QMainWindow ) : __init__ ( s e l f ) : QMainWindow . __init__ ( s e l f ) s e l f . s e t W i n d o w T i t l e ( ' Pegawai ' ) s e l f . r e s i z e (550 ,550) s e l f . l a b e l N a m a = QLabel ( s e l f ) s e l f . l a b e l A l a m a t = QLabel ( s e l f ) s e l f . l a b e l T g l L a h i r = QLabel ( " t g l self . labelJenis = QLabel ( s e l f ) s e l f . editNama = Q L i n e E d i t ( s e l f ) s e l f . e d i t A l a m a t = QTextEdit ( s e l f ) s e l f . e d i t T g l L a h i r = QDateEdit ( s e l f ) s e l f . e d i t J e n i s = QComboBox ( s e l f ) s e l f . b u t t o n S i m p a n = QPushButton ( s e l f ) s e l f . l i s t P e g a w a i = QTableWidget ( s e l f ) s e l f . l a b e l N a m a . s e t T e x t ( ' Nama ' ) s e l f . l a b e l A l a m a t . s e t T e x t ( ' Alamat ' ) s e l f . l a b e l J e n i s . setText ( ' Jenis lahir " , self )
K el a mi n ' )
s e l f . e d i t T g l L a h i r . s e t D i s p l a y F o r m a t ( ' dd M M yyyy ' ) s e l f . e d i t T g l L a h i r . s e t D a t e ( QDate ( 2 0 1 0 , 1 0 , 2 3 ) ) s e l f . e d i t J e n i s . a d d I t e m s ( [ ' P r i a ' , ' Wanita ' ] ) s e l f . b u t t o n S i m p a n . s e t T e x t ( ' Simpan ' ) s e l f . l i s t P e g a w a i . setColumnCount ( 5 ) s e l f . listPegawai . setHorizontalHeaderLabels ( [ ' ID ' , ' Nama ' , ' Alamat ' , ' T g l L a h i r ' , ' K el a mi n ' ] ) s e l f . l i s t P e g a w a i . setColumnWidth ( 0 , 2 0 )
s e l f . l a b e l T g l L a h i r . move ( 1 0 , 1 4 0 )
BAB 11.
102
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
def
closeEvent ( s e l f ,
for
s =
for
r =
in
' ' col () ) :
r a n g e ( s e l f . l i s t P e g a w a i . rowCount ( ) ) :
in
r a n g e ( s e l f . l i s t P e g a w a i . columnCount
v =
#r = r + v + ' , '
r += v +
' \n '
# chr (13)
event )
QMainWindow . c l o s e E v e n t ( s e l f ,
def
simpan ( s e l f ) : row = s e l f . l i s t P e g a w a i . rowCount ( ) p i d = row+1 s e l f . l i s t P e g a w a i . setRowCount ( p i d ) s e l f . l i s t P e g a w a i . s e t I t e m ( row , 0 , QTableWidgetItem ( s t r ( pid ) ) ) s e l f . l i s t P e g a w a i . s e t I t e m ( row , 1 , QTableWidgetItem ( s e l f . editNama . t e x t ( ) ) ) s e l f . l i s t P e g a w a i . s e t I t e m ( row , 2 , QTableWidgetItem ( s e l f . editAlamat . toPlainText ( ) ) ) s e l f . l i s t P e g a w a i . s e t I t e m ( row , 3 , QTableWidgetItem ( s e l f . editTglLahir . text () ) ) s e l f . l i s t P e g a w a i . s e t I t e m ( row , 4 , QTableWidgetItem ( s e l f . e d i t J e n i s . currentText () ) ) s e l f . editNama . c l e a r ( ) s e l f . editAlamat . c l e a r ( ) s e l f . e d i t J e n i s . setCurrentIndex (0) s e l f . editNama . s e t F o c u s ( )
BAB 11.
103
Bagaimana, panjang bukan ? Ya, contoh kali ini menyertakan class yang kerap digunakan. ulasan masing-masing class tersebut. Berikut ini
Menampilkan tulisan saja, tidak bisa diedit user. Untuk memasukkan tulisan, satu baris saja. Untuk memasukkan tulisan juga, tapi bisa lebih dari satu baris. Untuk mengisi tanggal. Untuk menetapkan pilihan. Tombol yang bila di-klik menjalankan suatu fungsi. Dalam hal
QTableWidget
ini menyimpan data pegawai ke QTableWidget yang ada di bawahnya. Menampilkan data berbentuk tabel.
Dari source tersebut dan menganalisa tampilan form, rasanya Anda bisa memahami cara penggunaan class di atas.
BAB 11.
104
Untuk mengetahui class lainnya beserta fungsi yang ada di dalamnya Anda bisa lihat di /usr/share/qt4/doc/html/classes.html. Gunakanlah browser untuk melihatnya.
Bab 12
Object Oriented Programming
Kita membuat fungsi dengan tujuan esiensi source, agar proses-proses yang sama dapat diwakili dengan memanggil fungsi tertentu. Begitu juga pada pembuatan objek. Mari mulai pada kasus. Ada sebuah le teks bernama barang.txt dengan isi seperti berikut ini. Listing 12.1: barang.txt
1 2 3
34 7
tetap
lebar kolom
rakter, dan harga barang 8 karakter atau sisanya. Anda diminta memindahkan data ini ke sebuah tabel di database. Mari mulai mempertimbangkan langkahlangkah yang bisa ditempuh. Andai saja kita bisa menggunakan split() yang bisa mengubah string menjadi list, sehingga dengan mudah kolom pertama ada kode barang, kolom kedua nama barang, dan seterusnya. Mengapa ? Perhatikan baris Mangga dimana nilai stok kosong. Ini artinya kolom ketiga menjadi harga barang. Tentu saja tidak konsisten dengan baris Jeruk dimana kolom ketiga adalah stok. memaksa. Baik, kita ikuti saja petunjuk pembuatnya dimana lebar kolom menjadi acuan. Sementara ini kita tidak perlu terlalu jauh bagaimana struktur tabelnya. Dari sudut pandang Python saja dulu, tipe data apa yang cocok untuk mewakili le ini. Untuk sementara anggap saja tipe data list yang sesuai karena ini merupakan bentuk tabel. Idealnya pembuat le itu mengubah programnya Namun posisi kita sedang tidak bisa agar kalau stok kosong diberi angka 0. Sayang sekali hal ini tidak dapat dilakukan.
105
BAB 12.
106
1 2 3 4 5 6 7 8 9 10 11
import for
in
f . readlines () : line [ : 3 ] . strip () line [3:3+10]. strip () l i n e [3+10:3+10+2]. s t r i p ( ) l i n e [3+10+2:]. s t r i p () nama , stok , harga ]
f . close ()
harga =
[ kode ,
Jalankan.
1 2 3 4
python
Tahap ini sudah baik, dimana setiap nilai sudah dapat diwakili dalam variabel kode, nama, stok, dan harga. Namun barang1.py masih kurang modular karena masukannya berupa nama le yang diberikan melalui command line.
12.1
Lebih Terstruktur
Kita perlu meningkatkan kadar modularitas dengan membuat fungsi. Buatlah barang2.py berikut.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
def
for
[]
line
in
harga = row =
[ kode , nama , s t o k , h a r g a ]
r o w s . append ( row )
return if
f . close () rows
__name__ ==
'__main__ ' :
BAB 12.
107
16 17 18 19
1 2 3 4
Hasil tetap sama, namun kini script tidak hanya mengandung fungsi tetapi juga bisa digunakan sebagai modul.
12.2
Lebih Umum
Sekarang aspek generalitas, atau tingkat ke-umum-an fungsi, dimana lebar setiap kolom sudah ditetapkan di dalam fungsi (hardcode). Bisakah ditingkatkan generalitasnya ? Kebutuhannya adalah ada le lain selain barang.txt dengan lebar setiap kolom berbeda dengan barang.txt, namun sifatnya masih sama yaitu
setiap
masing-masing 3, 10, 2, dan 8, mungkin pegawai.txt menggunakan 5, 30, 10, 50, dan 20. Jadi bukan hanya lebar kolomnya berbeda, jumlah kolomnya pun Karena sudah bersifat umum sebaiknya nama fungsi dan nama lenya pun umum. Buatlah xtable.py berikut.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
def
for
[]
line
awal = 0 row =
in
f . readlines () :
for
[]
width field
in
widths :
r o w s . append ( row )
return if
f . close () rows
__name__ ==
'__main__ ' :
BAB 12.
108
18 19 20 21
1 2 3 4
barang . t x t '9000 '] '8000 '] '10000 '] Anda bisa uji dengan membuat pega-
1 2 3 4 5 6
1BUMMI DWI PUTERA, 2ARIEF SETIADI 3CECEP ZAHRUDIN, 4NITA PANDRIA 5ILHAM 6MIRANDA Lalu xpegawai.py.
ST
1985 08 17L 1972 05 02L 1972 06 01L 1976 09 19P 1984 11 05L 1978 10 01P
ST
1 2 3 4 5 6 7
fixtable sys
import
in print
f i x t a b l e ( filename , row
Jalankan.
1 2 3 4 5 6 7
python
f i x p e g a w a i . py
pegawai . t x t ST ' , '1985 08 17 ' , 'L ' ] 'L ' ] 'P ' ] 'L ' ] '1972 05 02 ' , ST ' , '1976 09 19 ' , 'L ' ] 'P ' ]
[ '1 ' , [ '2 ' , [ '3 ' , [ '4 ' , [ '5 ' , [ '6 ' ,
'BUMMI DWI PUTERA, ' ARIEF SETIADI ' , 'CECEP ZAHRUDIN, ' NITA PANDRIA' , 'ILHAM' , 'MIRANDA' ,
'1972 06 01 ' ,
'1984 11 05 ' ,
'1978 10 01 ' ,
12.3
Tingkat Kerumitan
Kini Anda membutuhkan hasil yang
BAB 12.
109
kolom pertama, kode barang, bertipe integer kolom kedua, nama barang, bertipe string kolom ketiga, stok, bertipe integer kolom keempat, harga, bertipe integer
Lalu apa nilai yang cocok untuk stok Mangga yang tidak tertulis apapun alias string hampa ? Apa tetap diisi sebagai string hampa ? Sebaiknya tidak, karena kita akan menetapkan eld stok bertipe integer. Maka nilai yang cocok untuk string hampa adalah None, alias objek hampa. Buatlah le FixTableType.py berikut ini.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
from def
types
import
[]
ftypes ) :
for
line
awal = 0 row =
in
f . readlines () :
for
[] ftype =
width ,
a k h i r = awal + width
in
ftypes :
field
field field
f t y p e == I n t T y p e :
__name__ ==
import
fields
'__main__ ' :
sys = [
BAB 12.
110
33 34
for
$ python [1 , [2 , [3 ,
row
in print
f i x t a b l e ( filename , row
fields ) :
Jalankan.
1 2 3 4
barang . t x t
Perhatikan, tidak ada lagi kutip di kolom pertama, ketiga, dan keempat. Selanjutnya ada kebutuhan untuk menyimpan kembali data tersebut ke sebuah le sejenis, meski tidak harus ke barang.txt lagi. Fitur semakin bertambah dimana:
Tipe integer
Bab 13
Kerja Sampingan
Anda diminta membuat sebuah program yang bertugas mengendalikan sebuah GSM modem. Program ini bersifat daemon yang artinya selalu berjalan seolah tanpa akhir. Tugas utamanya adalah mengirim SMS yang berasal dari seluruh le yang ada di direktori /tmp/job. Hasil pengiriman SMS (berhasil / tidak) dikirim ke SMS gateway induk melalui XMLRPC, inilah kerja sampingannya atau sering disebut sebagai Mengapa kita perlu multi-thread ?
multi-thread
Modem tersebut hanya bisa mengirim sebuah SMS pada satu saat yang membutuhkan waktu 10 detik. Di sisi lain daemon ini harus mengabari status pengiriman ke SMS gateway induk melalui XMLRPC yang membutuhkan waktu 5 detik. Saat 1 2 3 4 Bahkan kalau terkena masalah bandwidth XMLRPC client ini bisa Thread 1 Mengirim SMS 1 selama 10 detik Mengabari status SMS 1 selama 5 detik Mengirim SMS 2 selama 10 detik Mengirim status SMS 2 selama 5 detik membutuhkan waktu 60 detik. Bayangkan kalau hanya single-thread.
Total waktu yang dibutuhkan untuk mengirim dua SMS adalah 30 detik. Sekarang bandingkan bila menggunakan multi-thread. Saat 1 2 3 Thread 1 Mengirim SMS 1 10 detik Mengirim SMS 2 10 detik Mengabari status SMS 1 5 detik Mengabari status SMS 2 5 detik Thread 2
Total waktu yang dibutuhkan adalah 10+10+5 = 25 detik, selisih 5 detik dari single-thread. Bagi SMS gateway itu merupakan jeda yang cukup berarti mengingat banyaknya SMS yang dikirim. Mari kita mulai simulasinya dengan membuat test_thread.py.
from
threading
import
111
BAB 13.
KERJA SAMPINGAN
112
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
time os
glob
import
glob
run ( s e l f ) :
' Kerja
sampingan
def
Thread . j o i n ( s e l f )
job_dir = kabar = []
' Pekerjaan
utama
dimulai
time . s l e e p ( 1 ) os . path . e x i s t s ( j ob _d ir ) :
filename = filenames [ 0 ]
BAB 13.
KERJA SAMPINGAN
113
45 46 47 48 49 50 51 52 53
print print
sampingan . j o i n ( ) Jalankan.
1 2 3
python
04:56:54 04:56:54
Sampai di sini ia menunggu keberadaan le di direktori /tmp/job. konsole lain dan buatlah direktori /tmp/job.
mkdir
/tmp/ j o b
echo
h e l l o > /tmp/ j o b / h e l l o . t x t
1 2
Pointer
Perlu diperhatikan variabel kabar yang bertipe list (baris 30). Ini adalah
abel bersama
1
adalah
vari-
sini berlaku apa yang disebut pointer yang artinya alokasi memori pada baris 30 dengan baris 10 s e l f . kabar = k
sama
del
s e l f . kabar [ 0 ]
s e l f . kabar =
s e l f . kabar [ 1 : ]
Namun teknik ini akan membuat alokasi memori yang baru dimana thread 2 tidak lagi menggunakan variabel kabar sebagaimana yang digunakan oleh thread 1. Mudah-mudahan Anda paham apa yang dimaksud dengan multi-thread ini.
Bab 14
SMS Gateway
SMS Gateway adalah salah satu produk RAB yang memanfaatkan Python dan PostgreSQL. Akses ke database menggunakan SQLAlchemy dan Elixir. Dirancang semodular mungkin agar mudah dipakai oleh sistem lainnya yang bukan Python, bukan PostgreSQL, bahkan bukan Linux. Produk ini juga menerapkan teknik event driven dan plug-in.
14.1
1
$ sudo
Pemasangan
a p t g e t install imgw
Proses instalasi akan meminta Anda menyesuaikan le /etc/im/gw/cong.py. tadi. File ini perlu diisi dengan otentikasi ke database yang sudah dibuat
db_url =
Kemudian jalankan:
sudo
dpkg r e c o n f i g u r e
imgw
Proses ini akan membuat tabel yang dibutuhkan ke database totalindo. Paket im-gw digunakan untuk hal yang berkaitan dengan database seperti menyimpan dan mengirim pesan. Daemonnya bisa Anda stop dan start dengan cara:
sudo
/ e t c / i n i t . d / imgw
restart
im-gw juga otomatis hidup saat komputer dihidupkan. Anda bisa memantau log-nya dengan cara:
1 2 3
sudo
tail
/ v a r / l o g / im /gw . l o g INFO INFO Start Start result job at dir pid / var / s p o o l / 1202
2011 01 18 2011 01 18
im / r e s u l t /
114
BAB 14.
SMS GATEWAY
115
2011 01 18 on port
INFO
Start
result
xmlrpc
server
Tekan Ctrl-C untuk mengakhiri. Tapi sebaiknya tetap terpantau, dan Anda bisa gunakan konsole lain untuk aktivitas berikutnya. Untuk mengirim dan menerima SMS yang sebenarnya dibutuhkan pengendali modem:
sudo
a p t g e t
install
immodem
Paket ini juga akan membuat tabel, menggunakan kongurasi yang sama dengan im-gw. Pasanglah modem GSM atau CDMA. Merk yang sudah teruji adalah Wavecom, iTegno, dan Multitech. USB device lebih disarankan karena mendukung hotplug, dimana saat modem ditancapkan pengendalinya otomatis aktif. Anda bisa periksa dengan perintah:
1 2 3
ps 5627
ax ?
| Sl
? SN
28464
Perhatikan /usr/bin/modem-hotplug, dialah yang memantau aktivitas pemasangan perangkat USB. Lalu ada juga /usr/bin/modem, daemon inilah yang dipanggil oleh modem-hotplug saat USB modem ditancapkan. Lalu bagaimana jika perangkatnya ditancapkan di serial port ? Anda pastikan dulu modem itu terpasang di serial port berapa. Keberadaan serial port bisa dilihat dengan perintah berikut:
1 2 3 4 5 6 7
$ [ [ [ [ [ [
dmesg a
grep
ttyS ttyS0 at at I /O 0 x 3 f 8 ( i r q = 4) is a is
I /O 0 x 3 f 8 at at at at
( i r q = 4)
I /O 0 x d 1 0 0 I /O 0 x d 2 0 0 I /O 0 x d 7 0 0 I /O 0 x d 8 0 0
1 6 5 5 0A 1 6 5 5 0A 1 6 5 5 0A 1 6 5 5 0A
Anda bisa menyebutkan semua serial port yang ada pada le /etc/im/modem/modem.conf:
1 2 3
[ device ] ; device S6 S7 di / dev / t t y S4 S5 p o r t = USB0 USB1 USB2 USB3 USB4 USB5 USB6 USB7 S0
BAB 14.
SMS GATEWAY
116
sudo
/ e t c / i n i t . d /modem
restart
1 2
ps
ax ?
grep
11211
ttyS0 untuk mengetahui serial port mana yg digunakan. Anda bisa mengesienkan modem.conf diatas dengan hanya mencantumkan S0 saja, namun tidak diubah pun tidak menjadi masalah. Sedangkan untuk melihat log-nya, terlebih dahulu Anda periksa direktori /var/log/modem, ada le apa di sana:
1 2
sudo
ls
/ v a r / l o g /modem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
sudo
tail
/ v a r / l o g /modem / 5 1 0 0 1 2 5 4 1 2 1 8 9 1 1 . l o g INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO INFO
30
2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18 2011 01 18
01:45:22 ,304 01:45:24 ,307 01:45:24 ,308 01:45:24 ,308 01:45:24 ,309 01:45:26 ,312 01:45:26 ,312 01:45:26 ,312 01:45:27 ,326 01:45:27 ,326 01:45:27 ,327 01:45:27 ,327 01:45:29 ,330 01:45:29 ,330 01:45:29 ,330 01:45:31 ,332 01:45:31 ,333 01:45:31 ,333 01:45:32 ,346 01:45:32 ,346 01:45:32 ,352
> < < < > < < > < < < > < < > < < > < <
AT + CSQ AT + CSQ + CSQ : OK AT +CLIP=1 AT +CLIP=1 OK AT + C G M M AT + C G M M MULTIBAND OK AT + CNMI= 0 , 1 , 1 , 1 , 0 AT + CNMI= 0 , 1 , 1 , 1 , 0 OK AT + C M G F =1 AT + C M G F =1 OK AT + CMGL ="ALL" AT + CMGL ="ALL" OK / d e v / ttyUSB1 at pid and / 28464 9 0 0E 1800 20 ,0
Serving
v a r / s p o o l / im / j o b /modem / 5 1 0 0 1 2 5 4 1 2 1 8 9 1 1 /
Bila Anda sudah berjumpa kalimat yang berawalan Serving seperti di atas, maka modem sudah siap.
14.1.1
IMEI Chip
Lalu apa yang dimaksud dengan angka 510012541218911 pada nama le log ? Mengapa tidak USB0.log atau S0.log saja ?
BAB 14.
SMS GATEWAY
117
Angka itu disebut dengan IMEI alias identitas chip / SIM card. Jika komputer membutuhkan informasi device port untuk mengendalikan modem, maka manusia / user membutuhkan IMEI sebagai identitas chip yang ada di dalam modem. Aktivitas menerima dan mengirim SMS tentu melekat pada chip, bukan pada modem. Karena itu penggunaan IMEI pada nama log adalah yang paling tepat untuk menjaga konsistensi history. Lagi pula device port yang digunakan modem USB kadang berubah. Saat ini mungkin modem dikenali di USB0. Coba Anda lepas dan pasang lagi di tempat yang sama, bisa jadi kini USB1 yang digunakannya.
14.1.2
Database
Saat /usr/bin/modem mulai mengendalikan sebuah modem, ia melaporkan ke /usr/bin/im-gw bahwa IMEI 510012541218911 baru saja hidup. Kejadian ini disebut sebagai startup. Saat itulah im-gw memeriksa keberadaan IMEI tersebut di tabel im.agent. Kalau belum ada ia tambahkan, dan kalau sudah ada ia perbaharui statusnya.
1 2 3 4 5 6 7 8 9 10
psql
ilham for
localhost ilham :
totalindo
user
(8.4.4) connection " help " id for ( cipher : help . status F R O M im . a g e n t ; | | status 0 DHE RSAAES256SHA, bits : 256)
t o t a l i n d o= > SELECT i d ,
+
510012541218911 (1 row ) Status 0 berarti modem siap, status negatif berarti sebaliknya. Alasan untuk status negatif bisa Anda lihat di tabel im.status.
14.2
Hello world!
1 2 3
t o t a l i n d o= > INSERT INTO im . a n t r i a n ( p e n e r i m a , p e s a n ) totalindo > SELECT INSERT 0 1 '+628179140068 ' , ' Hello world ! ' ;
Untuk penerima sesuaikanlah dengan nomor handphone Anda. Status pengiriman bisa Anda lihat di tabel im.selesai.
1 2 3
status ,
pengirim , |
penerima , |
pesan 1;
pengirim
BAB 14.
SMS GATEWAY
118
4 5 6
++++
1065 (1 | 0 | 510012541218911 | +628179140068 | Hello
world ! row ) Status 0 berarti telah terkirim, negatif sebaliknya, sedangkan positif berarti sedang diproses. Penjelasannya bisa dilihat di tabel im.status. Setelah Anda menerima SMS di handphone, balaslah SMS itu dengan:
Diterima
Tunggu sekitar 30 detik, dan lihat tabel im.antrian.
1 2 3 4 5 6
kirim ,
pengirim , |
penerima , |
pesan pesan
++++
1071 (1 row Mungkin Anda bertanya-tanya, saat mengirim pesan tabel im.antrian yang digunakan, begitu juga saat menerima pesan. Lalu apa pembedanya ? Perhatikan eld kirim di atas. Jika eld kirim = f (False), itu berarti record pesan masuk. Jika bernilai t (True) berarti pengiriman pesan. Defaultnya adalah True (pengiriman pesan). | f | +628179140068 | 510012541218911 |
Diterima
14.3
Ada dua paket utama di sini, yaitu im-gw dan im-modem. Keduanya terhubung sebagaimana pada gambar 14.1. Lalu mengapa harus ada dua paket ? Meski judul tulisan ini adalah SMS Gateway, namun pada konsepnya sistem ini dapat disanding dengan paket instant messenger seperti Yahoo! Messenger dan XMPP (Jabber, GTalk). Keduanya ada di paket im-ym dan im-xmpp. Jadi im-modem, im-ym, dan im-xmpp sejajar kedudukannya. Kalau im-modem memerlukan sik modem, maka im-ym dan im-xmpp membutuhkan koneksi Internet. Jadi bila Anda ingin mencoba sistem ini namun belum memiliki modem, maka bisa gunakan im-ym dan im-xmpp.
14.3.1
Yahoo! Messenger
Sebelum pemasangan, sebaiknya Anda siapkan Yahoo! account yang baru yang akan digunakan oleh /usr/bin/ym (daemon dari paket im-ym). pasang paketnya. Selanjutnya
BAB 14.
SMS GATEWAY
119
BAB 14.
SMS GATEWAY
120
sudo
a p t g e t
install
im ym
dimana inforab adalah Yahoo! account dan 1234 adalah passwordnya. Setelah disimpan tunggu satu menit dan periksa keberadaannya.
1 2
ps
ax ?
2339
1 2
sudo
tail
2011 02 25
14:24:46 ,729
Sekarang coba add buddy dari Yahoo! Messenger client seperti Pidgin. Tentu saja gunakan Yahoo! account yang lain. Daemon ym secara otomatis akan menambahkannya ke dalam daftar. Selanjutnya kirim pesan seperti biasa, lalu lihat log-nya:
2011 02 25
15:10:43 ,885
INFO I n b o x
1 2 3 4 5 6
kirim ,
jalur ,
pengirim ,
penerima ,
+++++
2074 (1 | f | 5 | info_rab | inforab | hello world
row ) Sekarang perhatikan kolom jalur yang berisi 5. Itu artinya jalur ym. Se-
dangkan jalur modem berisi 1 (default). Daftar jalur ini ada di tabel jalur.
14.3.2
XMPP
Jika Anda punya account di Gmail maka Anda dapat chatting dengan user Gmail lainnya. Namun sebenarnya Anda dapat chatting dengan user dari server lain yang menggunakan protokol XMPP seperti jabber.org atau jabber.rab.co.id. Ya, seperti email, protokol XMPP memungkinkan user dari server berbeda dapat saling mengirim pesan. Jika Anda berminat menggunakan XMPP untuk chatting dengan server, pasang paket im-xmpp.
sudo
a p t g e t
install
imxmpp
BAB 14.
SMS GATEWAY
121
1 2 3 4
u s e r s = { ' i n f o r a b @ g m a i l . com ' : { ' password ' : ' port ' : } Sesuaikan inforab dan 1234. perti ym. Jika Anda ingin menggunakan server lokal, bisa coba daftar ke jabber.rab.co.id menggunakan Pidgin. berikut. Lalu sesuaikan /etc/im/xmpp/cong.py seperti contoh Setelah disimpan tunggu satu menit hingga ' 1234 ' , ' sasl ' : True , 5223 , ' server ' : ' t a l k . g o o g l e . com ' }
daemon-nya up. Setelah hidup, Anda bisa lakukan pengujian yang serupa se-
1 2 3 4 5
u s e r s = { ' i n f o r a b @ g m a i l . com ' : { ' password ' : ' port ' : } Ya, im-xmpp juga dapat menghidupkan lebih dari satu account. Kebetulan jabber.rab.co.id menggunakan kongurasi yang lebih sederhana sebagaimana contoh di atas. ' 1234 ' , ' sasl ' : True , 5223 , ' server ' : ' t a l k . g o o g l e . com ' } , ' 1234 '
14.4
Broadcast
Anda telah memiliki banyak pelanggan dan ingin mengirim pesan yang sama ke mereka. Dengan mudah lakukan query berikut.
1 2
pesan )
Sayangnya untuk jumlah penerima yang sangat banyak tidak bisa semudah itu, karena /usr/bin/modem memiliki nilai job timeout yang default-nya 420 detik alias 7 menit. Jika setiap pesan membutuhkan waktu 10 detik untuk dikirim, maka hanya 42 pesan saja yang akan dikirim. Selebihnya akan dilaporkan oleh /usr/bin/modem sebagai status -9 alias Timeout. Menaikkan nilai timeout di /etc/im/modem/modem.conf bisa juga jadi solusi, namun tidak disarankan. Teknik yang paling pas adalah menyiapkan daemon baru yang memantau nilai eld job. Jika job lebih besar dari 5 maka tidak dilakukan INSERT ke im.antrian. Untuk kebutuhan ini sudah disiapkan paket im-broadcast.
sudo
a p t g e t
install
im b r o a d c a s t
Untuk mengirim pesan kita perlu melakukan INSERT ke dua tabel, yaitu im.broadcast dan im.broadcast_penerima. Tabel pertama berisi pesan, sedangkan tabel kedua berisi penerimanya. Kedua tabel ini akan diproses oleh daemon /usr/bin/im-broadcast untuk disalin ke tabel im.antrian. Pertama kita memerlukan nilai ID untuk broadcast yang baru.
BAB 14.
SMS GATEWAY
122
1 2 3 4 5
n e x t v a l ( ' im . b r o a d c a s t _ i d _ s e q ' ) ;
1 2 3
t o t a l i n d o= > INSERT INTO im . b r o a d c a s t ( i d , j u d u l , p e s a n ) totalindo > SELECT 8 , '; INSERT 0 1 ' Uji broadcast ' , ' Selamat transaksi
Field judul hanya untuk keterangan saja. Field pesan-lah yang nanti akan dikirim. Berikutnya tambahkan penerima pesan.
1 2 3
t o t a l i n d o= > INSERT INTO im . b r o a d c a s t _ p e n e r i m a ( i d , penerima ) totalindo > SELECT 8 , INSERT 0 1 '+628179140068 ';
Jalur default yang digunakan adalah 1 (modem). Untuk jalur lainnya sertakan eld jalur.
1 2 3
t o t a l i n d o= > INSERT INTO im . b r o a d c a s t _ p e n e r i m a ( i d , p e n e r i m a , jalur ) totalindo > SELECT INSERT 0 1 8 , ' info_rab ' , 5 ;
Indeks
__name__, 34 ALTER TABLE, 49, 50 Android, 4 apt-get install, 5 apt-get update, 5 autoincrement eld, 45 BlankOn, 5 break, 13 continue, 14 CREATE TABLE, 43 CREATE VIEW, 51 createdb, 42 createuser, 41 Data Denition Language, 50 Data Manipulation Language, 50 date(), 29 datestyle, 47 datetime(), 29 DEFAULT, 43 DELETE FROM, 49 DROP TABLE, 50 dropdb, 42 dropuser, 41 epoch, 28 eval(), 19 oat, 10 for, 15 formatting, 16, 18 fullpath, 53 rata kanan, 54 Gnome, 6 int(), 18, 26 raw_input(), 9, 26 sequence, 45 123 objek hampa, 38 ORDER BY, 47 ORDER BY, DESC, 48 pg_dump, 53 pg_restore, 53 plpgsql, 5 plpython, 5 postgresql.conf, 47 PRIMARY KEY, 43 print, 8, 19 psql, 5, 42 nextval(), 46 None, 38 NOT NULL, 43 Mac, 4 mktime(), 28 modul datetime, 29 modul locale, 36 modul time, 27 length(), 55 Linux, 4 locale-gen, 37 localtime(), 27 lower(), 8 lpad(), 55 integer, 10 KDE, 5, 6 konsole, 5
INDEKS
124
serial, 45 sisa pembagian, 17 SQLAlchemy, 5 string, 8, 10 sudo, 41 Symbian, 4 text editor, 6 time(), 28 tipe data dictionary, 25 tipe data oat, 17 tipe data integer, 17 tipe data list, 19 transaction, 71 transaction, auto commit, 71 type(), 16 Ubuntu, 5 Unix, 4 Unix time, 28 UPDATE, 49 upper(), 8 variabel, 10 vi, 6 while, 14