Anda di halaman 1dari 125

Database PostgreSQL, Pemrograman Python, dan SMS Gateway

RAB Linux Indonesia

2010

Daftar Isi

1 Alasan 2 Pemasangan 3 Hello World!


3.1 3.2 3.3 Kondisi

Variabel dan Tipe Datanya Perulangan

. . . . . . . . . . . . . . . . . . . . .

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

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Konversi Tipe Data Formatting Kalkulator . . . . . . . . . . . . . . . . . . . . .

16
16 17 18 18 19 19 20 21 24 25 27

Bilangan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Pemenggalan Keberadaan Elemen

Mengubah Elemen . . . . . . . . . . . . . . . . . . . . . .

Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Waktu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5 Modularitas
5.1 5.2 5.3

Membuat Fungsi Membuat Modul

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

30
32

31

Search Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

6 Fungsi
6.1 6.2 7.1

Memanggil Dirinya Sendiri Format Uang

. . . . . . . . . . . . . . . . . . . . .

36
36 36

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7 Database
7.1.1 7.1.2

Tabel

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mengubah Struktur

41
43 45 48

DAFTAR ISI

7.2 7.3 7.4

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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8 Python Akses Database


8.1 8.2 8.3 Synchronizer 8.3.1 8.3.2 8.4

Active Record . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Auto Commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aspek Keamanan . . . . . . . . . . . . . . . . . . . . . . . Tambah, Perbaharui, Hapus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62
70 73 75

63

72

Migrasi dari Database Lain

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

XMLRPC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . PHP sebagai XMLRPC Client Drupal . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10 Pengemasan

10.1 Paket Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Debian Repository . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 Remastering Ubuntu . . . . . . . . . . . . . . . . . . . . . . . . .

89
89 94 95

11 Graphical User Interface

11.1 Orientasi Objek . . . . . . . . . . . . . . . . . . . . . . . . . . . .

98
99

11.2 Daftar Pegawai . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

12 Object Oriented Programming


12.2 Lebih Umum

12.1 Lebih Terstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 12.3 Tingkat Kerumitan . . . . . . . . . . . . . . . . . . . . . . . . . . 108

105

13 Kerja Sampingan 14 SMS Gateway

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

14.3.2 XMPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

DAFTAR ISI

14.4 Broadcast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

Bab 1
Alasan

Mengapa Python merupakan bahasa yang tepat untuk pembuatan berbagai aplikasi ? Berikut ini alasannya.

Multiplatform Mudah Hemat

Python merupakan bahasa pemrograman yang tersedia di ber-

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).

Lalu mengapa PostgreSQL dipilih sebagai media penyimpanan datanya ?

Multiplatform Lengkap

PostgreSQL tersedia di Linux, Windows, Mac, dan Unix. Memili-

PostgreSQL tergolong lengkap dan mudah dikembangkan.

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

Kadang web server Apache tidak otomatis hidup:

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/

Kemudian di browser (sebaiknya Firefox) buka url:

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

Kemudian sesuaikan /etc/vim/vimrc,

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

" Vim5 and " line

later

versions the next

support

syntax by

highlighting .

Uncommenting enables on syntax

syntax

highlighting

default .

Saat Anda membuka le untuk keduakalinya, maka kursor langsung menuju ke lokasi sebelumnya.

1 2 3 4 5

" Uncomment position " if reopening au endif

the a

following

to

h a v e Vim jump

to

the

last

when file

h a s ( " autocmd " ) BufReadPost l i n e (" $ ") |

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

" Uncomment rules " " if detected filetype plugins .

the

following to the Per

to

h a v e Vim

load

indentation only 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

endif Pencarian mengabaikan huruf besar atau kecil (incasesensitive).

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:

python maka sambutannya seperti ini:

1 2 3 4 5

Python [GCC Type > > >

2.6.5

( r265 :79063 , on linux2 " copyright " ,

Apr

16

2010 , or

13:57:41) "license" for more

4.4.3] " help " ,

" credits "

information .

Mulailah dengan yang sederhana, menampilkan sebuah pesan menggunakan perintah print.

1 2

> > > print Hello

' 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

> > > print > > > print hello

' Hello ' Hello

World ! ' . u p p e r ( ) World ! ' . l o w e r ( )

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 ?

Lupakan dulu pemrograman berorientasi objek.

Untuk keluar

dari modus interaktif tekan Ctrl-D.

3.1

Variabel dan Tipe Datanya

Selain menggunakan kutip tunggal, string juga dapat dibatasi oleh kutip ganda:

1 2

> > > print Hari

" Hari

Jum ' a t "

Jum ' a t

Bila string lebih dari satu baris Anda bisa gunakan kutip tiga kali:

1 2 3 4 5 6

> > > print ... ... Hobi :

" " "Nama : B og o r

Bummi Dwi

Putera

Alamat :

Menggambar " " " Putera

Nama : Hobi :

Bummi Dwi B o go r Menggambar

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

nama = r a w _ i n p u t ( ' Nama :

') ') ')

a l a m a t = r a w _ i n p u t ( ' Alamat :

print print print


$

h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' Hobi : ' , ' , nama ' , alamat hobi ' Alamat :

Setelah disimpan jalankan:

python

p e g a w a i . py

Lalu masukkan data seperti yang diminta:

1 2 3

Nama : Hobi :

Bummi Dwi B o go r Menggambar

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 '

> > > umur = 24 nama + ( most recent

"< s t d i n >" , cannot

i n <module>

concatenate

Untuk mengatasinya jadikan variabel umur menjadi string:

1 2

> > > print Bummi usia

nama + 24

'

usia

' +

s t r ( umur ) +

'

tahun '

tahun

Atau dengan cara lain yg lebih mudah dibaca:

1 2

> > > print Bummi usia

'% s 24

u s i a %s tahun

tahun ' % ( nama ,

umur )

Meski string tidak bisa ditambah dengan integer, namun string bisa dikalikan dengan integer:

1 2

> > > print

' 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 :

print

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

python File ^ print

p e g a w a i . py line hobi indent 6 ' Hobi : ',

" 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

Script di atas berarti jika hobi diisi maka ditampilkan.

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

hobi : print ' Hobi : ', hobi

bisa juga ditulis dengan

1 2

if

hobi print

!=

' ': ', hobi

' 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

Tipe String Integer Float List Dictionary Objek

False  0 0 [] {} None

True (Contoh) 'abc' 1 1.2 [10,20,30] {'nama': 'Bummi', 'alamat': 'Bogor'} True

Tabel 3.1: Kondisi False di berbagai tipe data

1 2 3 4 5

> > > hobi = > > > hobi True > > > h o b i == False !=

' Menggambar ' '' ''

Lalu cobalah variabel hobi kosong.

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

> > > k o s o n g = h o b i == > > > print True kosong

''

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

nama = r a w _ i n p u t ( ' Nama :

') ') ')

a l a m a t = r a w _ i n p u t ( ' Alamat :

print print if print else print


:

h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' , nama ' , ' , alamat hobi hobi ' Alamat : ' Hobi :

hobi :

' Sebaiknya

diisi . ' Bagaimana jika kondisi

else digunakan untuk kondisi sebaliknya (False). tambahan ? hari Sabtu ditampilkan.

Misalkan jika hobinya menggambar maka pesan untuk hadir di

BAB 3.

HELLO WORLD!

13

Listing 3.3: pegawai2.py

1 2 3 4 5 6 7 8 9 10 11

nama = r a w _ i n p u t ( ' Nama :

') ') ')

a l a m a t = r a w _ i n p u t ( ' Alamat :

print print if print elif print else print


:

h o b i = r a w _ i n p u t ( ' Hobi : ' Nama : ' , nama ' , alamat ' Alamat :

h o b i . u p p e r ( ) == ' Datanglah ' Hobi : ' , hobi :

'M E N G G A M B A R' : di pelatihan gambar setiap Sabtu . '

hobi hobi diisi . '

' 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

while if not break


True :

Listing 3.4: pegawai3.py

nama = r a w _ i n p u t ( ' Nama : nama :

')

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 : ')

')

print print if print else print


:

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 '

!=

'S ' : ')

nama = r a w _ i n p u t ( ' Nama : ' Nama h a r u s

diisi . ' ')

a l a m a t = r a w _ i n p u t ( ' Alamat : alamat : ' Alamat harus

diisi . ' ')

h o b i = r a w _ i n p u t ( ' Hobi : ' , nama ' , ' , alamat hobi hobi ' Alamat : ' Hobi :

' Sebaiknya

diisi . ' "S" jika selesai : ' ) . upper ( )

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) :

print

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 ) :

print

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

Sudah dibahas sebelumnya bagaimana menuliskan data bertipe string.

> > > nama =

' Agus '

Atau bisa juga menggunakan kutip ganda.

> > > 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

> > > t y p e ( nama ) <t y p e ' s t r '>

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

> > > print Agus lahir

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

> > > print Agus lahir

'% 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

> > > print Keuntungan

' Keuntungan bulan ini

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.

> > > a = 10 a ditambah 2

1 2

>>> a + 2 12

a dikurang 4

1 2

> > > a 6

a dikali 5

1 2

> > > a 50

a dibagi dengan 3

1 2

> > > a 3

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

> > > a

3.0

3.3333333333333335 Untuk mendapatkan sisa pembagian (modulus) gunakan tanda persen ( % ).

1 2

> > > a % 3 1 a pangkat 2

1 2

> > > a 100

**

BAB 4.

TIPE DATA

18

4.2.1

Konversi Tipe Data

Konversi dari string menjadi bilangan bulat menggunakan fungsi int().

1 2

> > > 10

int ( '10 ')

int() juga dapat digunakan untuk pembulatan.

1 2

> > > 10

int (10.2)

Untuk pembulatan yang mendekati gunakan round().

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

> > > 10.5

float ( '10.5 ')

4.2.2

Formatting

Menuliskan bilangan ke dalam string bisa menggunakan 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

> > > print 3 + 2 = 5

'%d + %d = %d ' % ( a ,

b,

a+b )

Untuk bilangan pecahan (oat) gunakan %f.

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

>>> print '%.2 f + %.2 f = %.2 f ' % (a , b , a + b ) 3.50 + 2.00 = 5.50

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

while if not break print


$ 5 Hitung 49 Hitung 13 Hitung $ : : : python : Hitung

Listing 4.1: calc.py

True : : ') hitung :

h i t u n g = raw_input ( ' Hitung

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

' Bummi ' :

print

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

Gambar 4.1: Struktur list

1 2 3 4 5

> > > data = > > > print ' Bummi Dwi > > > print ' Bogor '

[ ' Bummi Dwi data [ 0 ] Putera ' data [ 1 ]

P u t e r a ' , ' Bogor ' , ' Menggambar ' , 2 4 ]

Tampilkan elemen terakhir:

1 2

> > > print 24

data [ 1]

Elemen kedua dari terakhir:

1 2

> > > print

data [ 2]

' Menggambar '

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

> > > print

data [ 0 : 2 ] Putera ' , ' Bogor ' ]

[ ' Bummi Dwi

Atau cukup ditulis tanpa 0:

1 2

> > > print

data [ : 2 ] Putera ' , ' Bogor ' ]

[ ' Bummi Dwi

Tampilkan 3 elemen terakhir:

1 2

> > > print [ ' Bogor ' ,

data [

3:]
24]

' Menggambar ' ,

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

elemen (untuk pemenggalan).

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 ' ]

while if not print break if in print else print


True : cari cari : Jalankanlah. $ python Cari Cari Tidak Cari buah : buah : buah : Ditemukan

= raw_input ( ' C a r i cari : ' Selesai ' daftar :

buah :

')

( ' Ditemukan ' ) ( ' Tidak ditemukan ' )

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
$

Listing 4.3: cat.py sys sys . argv

Jalankanlah.

1 2

python

c a t . py

[ ' c a t . py ' ] Jalankan lagi dengan input parameter.

1 2

python

c a t . py

/ etc / hosts

[ ' c a t . py ' ,

'/ etc / hosts ' ]

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
$

Listing 4.4: cat1.py sys

f i l e n a m e = sys . argv [ 1 ] f = open ( f i l e n a m e ) f . read ( ) f . close () Jalankan.

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

hosts localhost i p 6 l o c a l h o s t i p 6 l o o p b a c k i p 6 l o c a l n e t i p 6 m c a s t p r e f i x i p 6 a l l n o d e s ip6a l l r o u t e r s ip6a l l h o s t s

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

c a t 1 . py ( most recent line index call 3, last ) : i n <module> of range

Traceback

" c a t . py " , list

f i l e n a m e = sys . argv [ 1 ] IndexError : out

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

import if not print print

Listing 4.5: cat2.py

sys

sys . argv [ 1 : ] : ( ' Cara menggunakannya : p y t h o n %s <nama f i l e > ' % sys . argv [ 0 ] )

sys . exit () f i l e n a m e = sys . argv [ 1 ] f = open ( f i l e n a m e ) f . read ( ) f . close ()

BAB 4.

TIPE DATA

23

Jalankanlah tanpa input parameter,

1 2

python

c a t 2 . py python c a t . py <nama f i l e >

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

> > > daftar = > > > daftar []

[]

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 ' ,

' jeruk '

' jeruk ' ]

Selanjutnya mangga dihapus yang berarti elemen pertama atau index ke 0.

1 2 3

> > > del

daftar [ 0 ]

> > > daftar [ ' jeruk ' ]

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: '

' apel ' , mangga ' }

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 ' ,

while if not print break if in


True : key

k e y = r a w _ i n p u t ( ' Kode key : ' Selesai ' daftar :

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

p r o d u k 2 . py 5 5 9 9 tidak ditemukan tidak ditemukan

barang : barang barang : barang barang :

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

key = raw_input('Kode barang: ')


juga bertipe string. Lalu

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

print 'Kode barang', key, 'tidak ditemukan'


menjadi

print 'Kode barang', [key], 'tidak ditemukan'


Coba jalankan lagi.

1 2 3 4 5 6

python

p r o d u k 2 . py 9 [ '9 ']

Kode Kode tidak Kode

barang : barang barang :

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

Listing 4.8: produk3.py

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 ' ,

while if not print break if in print else print


True : key : $ python Kode Kode Kode apel Kode barang

k e y = r a w _ i n p u t ( ' Kode key : ' Selesai '

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

p r o d u k 3 . py 5 [5] 9 tidak ditemukan

barang : barang : barang :

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

> > > import > > > t

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

> > > t . tm_year 2010

BAB 4.

TIPE DATA

28

atau bulannya

1 2

> > > t . tm_mon 8 atau harinya

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

> > > ( akhir 16.0

awal )

24

60

60

Sehingga diperoleh 16 hari.

BAB 4.

TIPE DATA

29

Modul datetime
Menghitung hari lebih mudah menggunakan modul datetime.

1 2 3 4 5 6

> > > from

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

> > > from

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

> > > d . seconds 12

60

60

Bab 5
Modularitas

Mari kita buat program yang menghitung nilai faktorial. Berikut ini contohnya:

Apa itu faktorial ?

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

print while if not break if else for in * print


True : n: n = int (n) n < : 2: f = 1 f = 1 i f = f '%d !

Listing 5.1: faktorial1.py nilai ') faktorial '

' 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.

Berikutnya Anda diminta membuatnya dalam lingkungan

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

Listing 5.2: faktoria2l.py f a k t o r i a l (n) : n < 2: 1 r a n g e ( 1 , n+1) : i

return for in * return


f = 1 i f = f f True :

print while if not

' Menghitung

nilai ')

faktorial '

n = raw_input ( ' n = n:

BAB 5.

MODULARITAS

32

14 15 16 17

n = int (n)

break

print
$ n = 5

hasil

f a k t o r i a l (n) = %d ' % ( n , 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)

Sekarang buatlah faktorial.py.

1 2 3 4 5 6

def if return for in * return


a < i 2: a = a a

Listing 5.3: faktorial.py

faktorial (a) : 1 range (1 , a ) : i

Lalu buatlah cobafaktorial.py yang menggunakan modul faktorial ini.

from

faktorial

import

Listing 5.4: cobafaktorial.py faktorial

BAB 5.

MODULARITAS

33

2 3 4 5 6 7 8 9 10

print while if not break print


$ n = 5 5 ! = 120 n = 2 2! = 2 n = hasil

' Menghitung

nilai ')

faktorial '

True : n:

n = raw_input ( ' n =

n = int (n) = f a k t o r i a l (n) = %d ' % ( n , hasil ) '%d !

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

from faktorial import faktorial


source uji coba tersebut tidak dijalankan. Silahkan ubah faktorial.py menjadi berikut ini.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

def if return for in * return if print while if not break


a < i 2: a = a a

Listing 5.5: faktorial.py

faktorial (a) : 1 range (1 , a ) : i

__name__ == True :

'__main__ ' : nilai ') faktorial '

' Menghitung

n = raw_input ( ' n = n:

n = int (n)

BAB 5.

MODULARITAS

34

15 16

print
$ python

hasil

f a k t o r i a l (n) = %d ' % ( n , 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 =

Jadi perubahannya adalah pada

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

> > > import [ ' ' ,

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 ' ,

2.0 ' , 2.0 ' ,

' / 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

packages Supaya lebih yakin pindahkan sekalian.

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

> > > import > > > from

sys import faktorial

> > > s y s . p a t h . append ( ' / home/ s u g i a n a / l i b ' ) faktorial

Bab 6
Fungsi

6.1

Memanggil Dirinya Sendiri

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

faktorial (a) : a < 2: 1 a return

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

> > >

l o c a l e . format ( '%.0 f ' ,

10000 ,

True )

'10.000 ' Namun jika saat setlocale Anda menjumpai pesan kesalahan seperti ini:

36

BAB 6.

FUNGSI

37

1 2 3 4 5

Traceback File File

( most

recent line

call 1,

last ) : line 513 , in

"< s t d i n >" ,

i n <module>

" / u s r / l i b / p y t h o n 2 . 6 / l o c a l e . py " , _setlocale ( category , unsupported locale locale )

setlocale return l o c a l e . Error : setting

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

id_ID . UTF 8 upt o d a t e

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

Listing 6.1: uang.py 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 ' )

def return if print print print

uang ( n i l a i ,

p e c a h a n =2) : nilai , True )

l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n ,

__name__ ==

'__main__ ' :

uang ( 1 0 0 0 0 ) uang ( 1 0 0 0 0 . 3 ) uang ( 1 0 0 0 0 . 5 , 0 )

Cobalah.

1 2 3 4

python

uang . py

10.000 ,00 10.000 ,30 10.000 Perhatikan bagian

'%%.%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

Listing 6.2: uang.py 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 ' )

uang ( n i l a i , pecahan

if else return print print print

t y p e ( n i l a i ) == t y p e ( 0 ) : : l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n , nilai , True )

is

p e c a h a n=None ) : None :

pecahan = 0 pecahan = 2

if

__name__ ==

'__main__ ' :

uang ( 1 0 0 0 0 ) uang ( 1 0 0 0 0 . 3 ) uang ( 1 0 0 0 0 . 5 , 4 )

Jalankan lagi.

$ python uang.py 10.000 10.000,30 10.000,5000


Cermatilah baik-baik hasilnya. Di sini kita sudah mengenal fungsi yang memiliki dua masukan dan juga memiliki nilai default pada salah satu masukannya. Juga ada objek hampa bawaan Python bernama None. Rasanya fungsi uang kurang lengkap bila tidak disertai dengan mata uangnya. Kita akan jalankan skenario berikut ini: 1. Fungsi uang diberi satu masukan baru yaitu variabel tanda untuk memberi kesempatan programmer memasukkan mata uang seperti Rp, $, dst.

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 from def if

Listing 6.3: uang.py 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 ' ) types

import is

IntType

ribu ( nilai , pecahan

p e c a h a n=None ) : None :

if else return def if return if print print print print print print

t y p e ( n i l a i ) == I n t T y p e : : l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n , nilai , True )

pecahan = 0 pecahan = 2

uang ( n i l a i , tanda tanda =

is

p e c a h a n=None ,

t a n d a=None ) :

None : l o c a l e . l o c a l e c o n v ( ) [ ' currency_symbol ' ] ribu ( nilai , pecahan ) )

'%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,

print uang(10000.7, tanda='$')


dimana kita melewatkan variabel kedua, yaitu pecahan pada fungsi uang(),

def uang(nilai, pecahan=None, tanda=None):


Python tidak mempermasalahkan variabel pecahan dilewatkan, karena variabel ini telah diberi nilai default yaitu None.

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

> > > from '10.000 '

uang

import

uang

> > > uang ( 1 0 0 0 0 )

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

postgres Menurut ketentuan default, bila

Kini Anda sudah menjadi user postgres.

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

createuser it the the the n

ilham for new be be be a role : superuser ? to to ( y/n ) n databases ? more new ( y/n ) (

Enter Enter Shall Shall n Shall

password again : new new new

role role role

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

Selanjutnya membuat database:

createdb

ilham

totalindo

Ini artinya kita membuat database bernama totalindo yang dimiliki oleh user ilham. Untuk menghapusnya:

dropdb

totalindo

Untuk melihat daftar database bisa menggunakan psql:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

psql (8.4.4) " help " for help .

psql Type

p o s t g r e s= # \l List of Name databases | Owner | Encoding | Collation |

++++ 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 )

p o s t g r e s= # Untuk keluar gunakan \q atau tekan Ctrl-D:

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

Password psql SSL

user

(8.4.4) connection ( cipher : DHE RSAAES256SHA, bits : 256)

BAB 7.

DATABASE

43

5 6 7

Type

" help "

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

t o t a l i n d o (> PRIMARY KEY( i d ) NOTICE : CREATE TABLE pegawai_id_seq " index

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

" p e g a w a i " CREATE TABLE

totalindo=>
dan bedakan dengan prompt

totalindo(>

BAB 7.

DATABASE

44

yang bermakna prompt itu kelanjutan dari prompt sebelumnya. Penulisan

CREATE TABLE pegawai(


yang diikuti dengan penekanan tombol Enter membuat prompt memberitahukan Anda bahwa kurung buka masih aktif dan membutuhkan kurung tutup sebelum diakhiri dengan titik koma. Perintah dalam SQL (structured query language) sebenarnya tidak memperhatikan huruf besar / kecil (incasesensitive). Anda boleh menuliskan

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 |

pegawai " p u b l i c . pegawai " Type Modifiers |

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

Sequence mirip tabel, tepatnya

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.

tabel satu record

yang berisi data untuk ke-

1 2 3 4 5 6

t o t a l i n d o= > SELECT sequence_name pegawai_id_seq (1 row ) | |

F R O M pegawai_id_seq | | is_called f

; | |

last_value 1

+++

Sekarang kita lihat isi tabel pegawai.

1 2 3 4

t o t a l i n d o= > SELECT id (0 | nama |

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

p e g a w a i ( nama , t g l _ l a h i r ) or near "INT"

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

t o t a l i n d o= > SELECT id | nama

F R O M pegawai ; | tgl_lahir

++ 1 | Bummi Dwi P u t e r a | 1985 08 17


(1 row ) Perhatikan saat INSERT tadi eld id tidak disertakan, namun perintah SELECT memperlihatkan bahwa eld id terisi dengan angka 1. Inilah yang disebut dengan eld autoincrement. Lalu apa yang terjadi dengan sequence pegawai_id_seq ?

1 2 3 4 5 6

t o t a l i n d o= > SELECT sequence_name pegawai_id_seq (1 row ) | |

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

++ 1 | Bummi Dwi P u t e r a | 1985 08 17 2 | Arief Setiadi | 1972 05 02


(2 rows ) Perhatikan kembali eld id yang terisi dengan angka 2, dan lihat juga pegawai_id_seq.

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

sequence_name pegawai_id_seq (1 row )

| |

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

Carilah kata datestyle.

datestyle = 'iso, dmy'


Jika sudah tampak dmy seperti di atas maka Anda diizinkan mengisi eld tanggal dengan format tanggal-bulan-tahun. Namun jika Anda mendapati isinya

datestyle = 'iso, mdy'


maka ubahlah mdy menjadi dmy, simpan, logout semua psql, dan restart PostgreSQL.

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

++ 1 | Bummi Dwi P u t e r a | 1985 08 17 2 | Arief Setiadi | 1972 05 02 3 | Cecep Z a h r u d i n | 1972 06 01


(3 rows ) Agar tampil urut sesuai abjad gunakan ORDER BY.

BAB 7.

DATABASE

48

1 2 3 4 5 6 7

t o t a l i n d o= > SELECT id | nama

F R O M p e g a w a i ORDER BY nama ; | tgl_lahir

++ 2 | Arief Setiadi | 1972 05 02 1 | Bummi Dwi P u t e r a | 1985 08 17 3 | Cecep Z a h r u d i n | 1972 06 01


(3 rows ) Gunakan WHERE untuk mendapatkan record tertentu.

1 2 3 4 5

t o t a l i n d o= > SELECT id | nama

F R O M pegawai W H E R E id = 1; | tgl_lahir

++ 1 | Bummi Dwi P u t e r a | 1985 08 17


(1 row ) Tampilkan pegawai yang lahir di tahun 1972 dan diurutkan mulai yang tertua.

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

++ 2 | Arief Setiadi | 1972 05 02 3 | Cecep Z a h r u d i n | 1972 06 01


(2 rows ) Gunakan DESC pada ORDER BY jika ingin diurut mulai yang termuda. Tekan tombol panah atas untuk mengulang perintah sebelumnya dan tambahkan DESC.

1 2 3 4 5 6 7 8

t o t a l i n d o= > SELECT ORDER BY t g l _ l a h i r id | nama

F R O M pegawai t g l _ l a h i r ) = 1972 tgl_lahir

W H E R E date_part ( ' year ' , DESC ; |

++ 3 | Cecep Z a h r u d i n | 1972 06 01 2 | Arief Setiadi | 1972 05 02


(2 rows )

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

t o t a l i n d o= > ALTER TABLE p e g a w a i ADD p r i a DEFAULT t r u e ; ALTER TABLE

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

t o t a l i n d o= > SELECT id | nama

F R O M p e g a w a i ORDER BY nama ; | tgl_lahir | pria

+++ 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

t o t a l i n d o= > UPDATE p e g a w a i SET UPDATE 1 t o t a l i n d o= > SELECT id | nama

pria = False W H E R E id = 4;

F R O M p e g a w a i ORDER BY nama ; | tgl_lahir | pria

+++ 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

t o t a l i n d o= > DELETE F R O M pegawai W H E R E id = 2; DELETE 1 t o t a l i n d o= > SELECT id | nama

F R O M p e g a w a i ORDER BY nama ; | tgl_lahir | pria

+++

BAB 7.

DATABASE

50

6 7 8 9
(3

1 3 4

| | |

Bummi Dwi Cecep Nita

Putera

| | |

1985 08 17 1972 06 01 1976 09 19

| | |

t t f

Zahrudin Pandria

rows ) Jika Anda kurang suka dengan eld pria yang bertipe boolean bisa dihapus

dengan perintah ALTER TABLE.

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:

usia = tgl sekarang - tgl lahir


Lalu kita tampilkan dengan urutan diawali yang paling tua.

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

t o t a l i n d o= > SELECT i d , id 2 3 | | | Arief Cecep nama Setiadi Zahrudin

nama , | | |

now ( )

tgl_lahir usia

AS

usia

totalindo > F R O M p e g a w a i ORDER BY t g l _ l a h i r ;

++
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

kita ganti rumusnya:

usia = tahun sekarang - tahun kelahiran


sehingga query-nya menjadi:

1 2 3 4 5 6 7 8 9 10

t o t a l i n d o= > SELECT i d , tgl_lahir ) id 2 3 4 1 (4 | | | | | Arief Cecep Nita AS usia

nama , from now ( ) )

totalindo > ex tract ( year

ext ract ( year

from

totalindo > F R O M p e g a w a i ORDER BY t g l _ l a h i r ; nama Setiadi Zahrudin Pandria Putera | | | | | usia 38 38 34 25

++

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 ,

ext ract ( year

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

t o t a l i n d o= > SELECT id 2 3 4 1 (4 | | | | | Arief Cecep Nita nama Setiadi

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

t o t a l i n d o= > SELECT id | nama

F R O M v_pegawai ORDER BY nama ; | usia

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

Backup dan Restore

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 ) ) ;

Listing 7.1: pegawai.sql

INSERT INTO INSERT INTO INSERT INTO INSERT INTO


Dwi

p e g a w a i ( nama , t g l _ l a h i r , p r i a ) p e g a w a i ( nama , t g l _ l a h i r , p r i a ) p e g a w a i ( nama , t g l _ l a h i r , p r i a ) ) ; ) ; p e g a w a i ( nama , t g l _ l a h i r , p r i a )

P u t e r a ' , ' 1985 8 17 ' ,

S e t i a d i ' , ' 1972 5 2 ' ,

Z a h r u d i n ' , ' 1972 6 1 ' , P a n d r i a ' , ' 1976 9 19 ' ,

true true false


) ;

true

) ;

VALUES VALUES VALUES VALUES

( ' Bummi ( ' Arief ( ' Cecep ( ' Nita

Lalu di psql jalankan le tersebut:

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

p s q l : s q l / pegawai . s q l : 7 : sequence " pegawai . i d "

p s q l : s q l / pegawai . s q l : 7 : KEY will create table " pegawai " 1 1 1 1

CREATE TABLE / PRIMARY " p e ga w ai _ pk e y " for

implicit

index

4 5 6 7 8

CREATE TABLE INSERT 0 INSERT 0 INSERT 0 INSERT 0

Kalau Anda menemui kegagalan

BAB 7.

DATABASE

53

No such file or directory


cobalah menuliskan nama le secara fullpath, misalnya.

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 .

dan restore dengan psql:

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

(8.4.4) " help "

p o s t g r e s= # \l List of databases | Owner | Encoding | Collation | Name

++++ 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 (8.4.4) " help " for help .

psql Type

p o s t g r e s= # \l List of databases | Owner | Encoding | Collation | Name

++++ 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

t o t a l i n d o= > SELECT now ( ) ; now

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

t o t a l i n d o= > SELECT l p a d ( ' A' , 5 ) ; lpad

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

t o t a l i n d o= > SELECT length

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

t o t a l i n d o= > SELECT l p a d ( ' 1 ' , 5 , ' 0 ' ) ; lpad

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.

Namun Anda perlu memasangnya

sudo

su

postgres

" createlang

plpgsql

totalindo "

BAB 7.

DATABASE

56

Selanjutnya siapkanlah dua konsole, yang pertama untuk

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

Listing 7.2: hello.sql REPLACE FUNCTION hello ()

RETURNS t e x t

LANGUAGE p l p g s q l

END
$$ ;

' Hello

world . ' ;

Setelah disimpan jalankan di psql tadi:

1 2

t o t a l i n d o= > \i CREATE FUNCTION lalu cobalah

hello . sql

1 2 3 4 5

t o t a l i n d o= > SELECT hello

hello () ;

Hello (1 row ) world .

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

float NOT NULL DEFAULT

0;

Kemudian untuk mencatat aktivitas transaksi kita memerlukan tabel transaksi berikut ini.

1 2 3

CREATE TABLE PRIMARY KEY integer NOT NULL


transaksi ( id serial pid

Listing 7.3: transaksi.sql

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

UPDATE p e g a w a i SET INSERT INTO

s a l d o = s a l d o + 3000000 W H E R E id = 1;

t r a n s a k s i ( pid , ket , nominal , s a l d o ) 11 2010 ' ,3000000 , s a l d o

SELECT i d , ' GAJI F R O M pegawai 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

id F R O M t r a n s a k s i ORDER BY i d DESC LIMIT

1;

Artinya dapatkan record terakhir dari tabel transaksi.

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

CREATE OR integer float


p_pid p_ket , text , p_nominal

Listing 7.4: functransaksi.sql transaksi (

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

$$ rec record ; pegawai

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 ;

s a l d o = s a l d o + p_nominal i d = p_pid ; transaksi ( ket , saldo ) p_ket , saldo p_pid , p_nominal ,

nominal ,

i d = p_pid ;

END
$$

Restore script ini.

1 2

t o t a l i n d o= > \i CREATE FUNCTION

functransaksi . sql

Misalkan ID pegawai 1 meminjam (cash bon) sebesar Rp200.000,- maka:

1 2 3 4 5

t o t a l i n d o= > SELECT transaksi

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

t o t a l i n d o= > SELECT id | pid | | nominal

F R O M transaksi ; tgl | ket |

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

CREATE OR integer float integer AS integer UPDATE SET WHERE


p_pid p_ket , text , p_nominal RETURNS $$ LANGUAGE p l p g s q l DECLARE tid BEGIN ; pegawai

Listing 7.5: functransaksi.sql transaksi (

REPLACE FUNCTION

s a l d o = s a l d o + p_nominal i d = p_pid ;

tid = nextval ( ' transaksi_id_seq ' ) ;

INSERT INTO SELECT FROM WHERE


id , pid ,

transaksi ( ket , saldo ) p_ket , saldo p_pid , p_nominal ,

nominal ,

tid ,

pegawai i d = p_pid ;

END
$$

RETURN t i d ;

Restore.

1 2

t o t a l i n d o= > \i CREATE FUNCTION

transaksi . sql

Kini kita coba ID pegawai 5 bernama Ilham untuk transaksi.

1 2 3 4 5

t o t a l i n d o= > SELECT transaksi

t r a n s a k s i ( 5 , ' GAJI

11 2010 ' ,1500000) ;

3 (1 row )

BAB 7.

DATABASE

60

Lalu lihat hasilnya.

1 2 3 4 5 6 7

t o t a l i n d o= > SELECT id | pid | | nominal

F R O M transaksi ; tgl | ket |

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

Listing 7.6: uang.sql REPLACE FUNCTION uang ( n

RETURNS t e x t

float

LANGUAGE p l p y t h o n u import uang

uang ( n )

CREATE OR AS from
$$ ; $$ uang return

REPLACE FUNCTION uang ( n

RETURNS t e x t

integer

LANGUAGE p l p y t h o n u import uang

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 :

CREATE FUNCTION CREATE FUNCTION Lalu cobalah.

1 2 3 4 5

t o t a l i n d o= > SELECT uang ( 1 0 0 0 0 ) ; uang

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

Selanjutnya masuk ke modus interaktif untuk mencoba.

1 2 3 4 5

> > > import > > > url =

sqlalchemy

as

sa

' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / t o t a l i n d o '

> > > 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 ( ) <s q l a l c h e m y . e n g i n e . b a s e . C o n n e c t i o n object at 0 x 8 b 0 8 7 2 c>

Sampai di sini Python sudah terhubung ke PostgreSQL dan untuk melihat record tabel pegawai.

berhasil dengan perintah connect(). Anda bisa lanjutkan dengan perintah query

uji coba login

1 2 3 4 5 6 7 8 9

> > > > > > ... ... (1 , (2 , (3 , (4 ,

s q l = "SELECT for r in r q:

F R O M pegawai "

> > > q = db . e x e c u t e ( s q l ) print

' Bummi Dwi ' Arief ' Cecep ' Nita

Putera ' ,

datetime . date (1985 , 5, 6, 9,

8, 2) ,

17) ,

True )

Setiadi ' , Zahrudin ' , Pandria ' ,

datetime . date (1972 , datetime . date (1972 , datetime . date (1976 ,

True ) True ) False )

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.

PYTHON AKSES DATABASE

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

Bahkan eld id dan nama bisa dianggap variabel milik r:

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

> > >

s q l = "UPDATE p e g a w a i SET nama = u p p e r ( nama ) " object at 0 xb748a9ac>

> > > 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 "

> > > q = db . e x e c u t e ( s q l ) print

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

1 ;BUMMI DWI PUTERA,

ST; 1 9 8 5 0 8 1 7 ; t

2 ; ARIEF SETIADI ; 1 9 7 2 0 5 0 2 ; t 3 ;CECEP ZAHRUDIN; 1 9 7 2 0 6 0 1 ; t

BAB 8.

PYTHON AKSES DATABASE

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

Password psql SSL Type

user

(8.4.4) connection " help " for ( cipher : help . DHE RSAAES256SHA, bits : 256)

t o t a l i n d o= > SELECT id | nama

F R O M pegawai ; | tgl_lahir | pria

+++ 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 =

Listing 8.2: updatepegawai.py sqlalchemy as sa

' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / t o t a l i n d o '

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

f = open ( f i l e n a m e ) line pid , nama ,

in

BAB 8.

PYTHON AKSES DATABASE

65

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

pid = i n t ( pid ) p r i a = p r i a == s q l = "SELECT 't '

F R O M pegawai W H E R E i d = %d " % p i d

# Agar boolean True / False

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 , :

s q l = "INSERT INTO p e g a w a i " ( id , nama , "SELECT %d , s q l = s q l % ( pid , '% s ' , nama ,

" + \ pria ) " + \ pria )

tgl_lahir ,

'% s ' , %s " 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

Kemudian ke psql, lihat tabel pegawai.

1 2 3 4 5 6 7 8 9

t o t a l i n d o= > SELECT id | nama

F R O M p e g a w a i ORDER BY i d ; | tgl_lahir | pria

+++ 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

1 ;BUMMI DWI PUTERA, 3 ;CECEP ZAHRUDIN,

ST; 1 9 8 5 0 8 1 7 ; t

2 ; ARIEF SETIADI ; 1 9 7 2 0 5 0 2 ; t ST; 1 9 7 2 0 6 0 1 ; t

BAB 8.

PYTHON AKSES DATABASE

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
\

Listing 8.4: updatepegawai1.py Entity , using_options , setup_all ,

session

class

Pegawai ( E n t i t y ) :

u s i n g _ o p t i o n s ( t a b l e n a m e= ' p e g a w a i ' , a u t o l o a d=True ) ' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /

metadata . bind = totalindo ' setup_all () filename =

' pegawai . csv ' f . readlines () : tgl_lahir , pria = line . strip () . s p l i t ( ' ; ' )

for

f = open ( f i l e n a m e ) line pid , nama ,

in

pid = i n t ( pid ) 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 else

p . count ( ) : r = p . one ( ) : r = Pegawai ( ) r . id = pid

r . nama = nama r . tgl_lahir = tgl_lahir r . p r i a = p r i a == s e s s i o n . commit ( ) Jalankanlah. 't '

python

u p d a t e p e g a w a i 1 . py

Lalu lihat hasilnya di psql.

1 2 3 4 5 6

t o t a l i n d o= > SELECT id | nama

F R O M p e g a w a i ORDER BY i d ; | tgl_lahir | pria

+++ 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.

PYTHON AKSES DATABASE

67

7 8 9 10
(6

4 5 6

| |

NITA PANDRIA ILHAM

| | |

1976 09 19 1984 11 05 1978 10 01

| | |

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
\

Listing 8.5: deletepegawai.py Entity , using_options , metadata ,

import class if not try except


:

session

Pegawai ( E n t i t y ) :

u s i n g _ o p t i o n s ( t a b l e n a m e= ' p e g a w a i ' , a u t o l o a d=True ) sys . argv [ 1 : ] : p y t h o n %s <i d > ' % s y s . a r g v [ 0 ] )

s y s . e x i t ( ' Caranya :

pid = i n t ( sys . argv [ 1 ] ) ValueError : pegawai harus angka ' ) s y s . e x i t ( ' ID

metadata . bind = totalindo ' setup_all ()

' 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

p . count ( ) : p e g a w a i %d tidak ada ' % p i d )

s y s . e x i t ( ' ID

r = p . one ( ) r . delete ()

print
$ ID

s e s s i o n . commit ( ) ' ID p e g a w a i %d sudah dihapus ' % pid

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

Periksa hasilnya di psql.

BAB 8.

PYTHON AKSES DATABASE

68

1 2 3 4 5 6 7 8 9

t o t a l i n d o= > SELECT id | nama

F R O M p e g a w a i ORDER BY i d ; | tgl_lahir | pria

+++ 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

from import class

elixir sys

setup_all

import

Listing 8.6: selectpegawai.py Entity , using_options , metadata ,

Pegawai ( E n t i t y ) :

u s i n g _ o p t i o n s ( t a b l e n a m e= ' p e g a w a i ' , a u t o l o a d=True ) ' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /

metadata . bind = totalindo ' setup_all ()

for

in print
r

Pegawai . quer y . a l l ( ) : r . id , r . nama , r . tgl_lahir , r . pria

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

Listing 8.7: selectpegawai2.py Entity , using_options , metadata ,

BAB 8.

PYTHON AKSES DATABASE

69

4 5 6 7 8 9 10 11

class

Pegawai ( E n t i t y ) :

u s i n g _ o p t i o n s ( t a b l e n a m e= ' p e g a w a i ' , a u t o l o a d=True ) ' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /

metadata . bind = totalindo ' setup_all ()

for
$

in print
r

P e g a w a i . q u e r y . o r d e r _ b y ( ' nama ' ) : r . id , r . nama , r . tgl_lahir , r . pria

Jalankan.

1 2 3 4 5 6

python

s e l e c t _ p e g a w a i . py True True True ST 1985 08 17 True False

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

Ingin yang lebih rapi ? Ubahlah seperti ini.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

from import class

elixir sys

import

Listing 8.8: selectpegawai.py

Pegawai ( E n t i t y ) :

u s i n g _ o p t i o n s ( t a b l e n a m e= ' p e g a w a i ' , a u t o l o a d=True ) ' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /

metadata . bind = totalindo ' setup_all () jenis = { True : False :

' Pria ' , ' Wanita ' }

p = P e g a w a i . q u e r y . o r d e r _ b y ( ' nama ' )

for

in print
r

p: str ( r . id ) . center (3) , \ \ \

r . nama . l j u s t ( 2 0 ) , r . tgl_lahir , jenis [ r . pria ]

Jalankan.

1 2 3 4

$ 2 1 3

python

s e l e c t _ p e g a w a i . py 1972 05 02 ST 1985 08 17 1972 06 01 ST Pria Pria Pria

ARIEF SETIADI BUMMI DWI PUTERA, CECEP ZAHRUDIN,

BAB 8.

PYTHON AKSES DATABASE

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) ,

Package : Version : Depends :

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 =

Listing 8.9: transaksi.py sqlalchemy sys ' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / t o t a l i n d o ' as sa

db = s a . c r e a t e _ e n g i n e ( u r l )

print try except


:

' Transaksi ' pegawai : ')

p i d = r a w _ i n p u t ( ' ID pid = i n t ( pid ) ValueError : s y s . e x i t ( ' ID

p e g a w a i %s

tidak

benar ' % pid )

s q l = "SELECT nama 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 not print if not

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

k e t = raw_input ( ' Keterangan : ket : s y s . e x i t ( ' Keterangan

harus

BAB 8.

PYTHON AKSES DATABASE

71

24 25 26 27 28 29 30 31 32

try except
:

n o m i n a l = r a w _ i n p u t ( ' Nominal : nominal = f l o a t ( nominal ) tidak

')

ValueError : benar ' % nominal ) ket ,

s y s . e x i t ( ' Nominal %s

s q l = "SELECT nominal )

t r a n s a k s i (%d , ' % s ' , % . 2 f ) " % ( p i d ,

q = db . e x e c u t e ( s q l )

print

' 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

Transaksi pegawai : Nama : CECEP ZAHRUDIN, 4500000 berhasil ,

Keterangan : Nominal : Transaksi

Untuk memastikan transaksi benar-benar berhasil, lihatlah di psql.

1 2 3 4 5 6 7

t o t a l i n d o= > SELECT id | pid | tgl

F R O M transaksi ; | ket | nominal | saldo

+++++ 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

. Ini bisa diartikan SQLAlchemy atau mungkin Psycopg2

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

import from import


url =

sqlalchemy . s q l . expression

import

text

' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / t o t a l i n d o '

BAB 8.

PYTHON AKSES DATABASE

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 )

print try except


:

' Transaksi ' pegawai : ')

p i d = r a w _ i n p u t ( ' ID pid = i n t ( pid ) ValueError : s y s . e x i t ( ' ID

p e g a w a i %s

tidak

benar ' % pid )

s q l = "SELECT nama 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 not print if not


:

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

k e t = raw_input ( ' Keterangan : ket : s y s . e x i t ( ' Keterangan

harus

try except print

n o m i n a l = r a w _ i n p u t ( ' Nominal : nominal = f l o a t ( nominal ) tidak

ValueError : benar ' % nominal ) ket ,

s y s . e x i t ( ' Nominal %s

s q l = "SELECT nominal )

t r a n s a k s i (%d , ' % s ' , % . 2 f ) " % ( p i d , a u t o c o m m i t=True ) ) ID ' ,

q = db . e x e c u t e ( t e x t ( s q l , ' Transaksi

berhasil ,

q . fetchone () . transaksi

Penambahan pada baris 2:

from sqlalchemy.sql.expression import text


dan perubahan pada baris 32 dari

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.

PYTHON AKSES DATABASE

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

IP publik di kantor pusat

agar synchronizer di

IP publik ini dapat dipesan ke ISP

(Internet Service Provider) bersangkutan. Setelah IP publik didapat, tambahk-

/etc/postgresql/8.4/main/pg_hba.conf:

host all all 0.0.0.0/0 md5


Kongurasi ini artinya semua host dapat mengakses database pusat, tentunya dengan otentikasi username dan password. Simpan dan restart daemon-nya:

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

Di script Python gantilah localhost dengan IP publik tadi.

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

. Dengan kata lain pusat-lah yang membaca database outlet, sedangkan

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.

PYTHON AKSES DATABASE

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 Link inet e n c a p : UNSPEC addr : 1 0 . 8 . 0 . 6 HWaddr P t P : 1 0 . 8 . 0 . 5 Mask M T U

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.

Jangan lupa untuk menguji konektivitas network

dengan perintah ping. OpenVPN telah terpasang dan kedua host pusat dan outlet sudah bisa saling ping. Selanjutnya apa yang harus dilakukan ?

di outlet

Tambahkan baris berikut ini di .

/etc/postgresql/8.4/main/pg_hba.conf

host all all 10.8.0.1/32 md5


Lalu restart daemon-nya.

1 2

sudo

service

postgresql

8.4

restart server

Restarting

PostgreSQL

8.4

database

Di pusat lakukan uji konektivitas dengan psql.

psql

ilham

10.8.0.6

totalindo

Jika sudah terhubung baik, saatnya membuat script synchronizer.

BAB 8.

PYTHON AKSES DATABASE

75

8.3.2

Tambah, Perbaharui, Hapus

Adalah sebuah tabel produk dengan struktur sebagai berikut:

1 2 3 4 5

create table
id serial nama name harga ) ;

Listing 8.11: produk.sql produk (

primary key not null unique float not null


,

1 2 3

INSERT INTO INSERT INTO INSERT INTO


,10500; ,9250; ,14300;

Isilah dengan beberapa record berikut ini: p r o d u k ( nama , h a r g a ) p r o d u k ( nama , h a r g a ) p r o d u k ( nama , h a r g a )

SELECT SELECT SELECT

' Jeruk ' Salak ' Mangga

Pontianak ' Pondoh ' Indramayu '

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

import from from from

Listing 8.12: sync.py sqlalchemy

elixir

metadata

import

as

sa using_options , scoped_session , setup_all , sessionmaker

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

e s = sa . create_engine ( ' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / totalindo ' )

BAB 8.

PYTHON AKSES DATABASE

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

e t = sa . create_engine ( ' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / totalindo2 ' ) s s = s c o p e d _ s e s s i o n ( s e s s i o n m a k e r ( b i n d=e s ) ) s t = s c o p e d _ s e s s i o n ( s e s s i o n m a k e r ( b i n d=e t ) ) ms = m e t a d a t a mt = T h r e a d L o c a l M e t a D a t a ( ) ms . b i n d = e s mt . b i n d = e t

class class

ProdukS ( E n t i t y ) : a u t o l o a d=True , m e t a d a t a=ms , s e s s i o n=s s )

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 ' ,

ProdukT ( E n t i t y ) : a u t o l o a d=True , m e t a d a t a=mt , s e s s i o n=s t )

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 ()

# INSERT & UPDATE

for

all_ps = ps

in print try print except print


:

[]

ProdukS . q u e r y . a l l ( ) : ps . id , p s . nama , ps . harga ,

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 )

p t . nama = p s . nama pt . harga = ps . harga s t . commit ( ) a l l _ p s . append ( 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 ( )

ProdukT . q u e r y . a l l ( ) : all_ps : p t . nama , pt . harga , ' dihapus ' pt . id ,

BAB 8.

PYTHON AKSES DATABASE

77

8.4

Migrasi dari Database Lain

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

Lalu buatlah dbmigration.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 33 34 35 36

import from from from import

Listing 8.13: dbmigration.py sqlalchemy as

s q l a l c h e m y . schema s q l a l c h e m y . orm elixir sys

import

import import

sa

ThreadLocalMetaData sessionmaker

scoped_session ,

dbs = s a . c r e a t e _ e n g i n e ( s y s . a r g v [ 1 ] ) dbt = s a . c r e a t e _ e n g i n e ( s y s . argv [ 2 ] ) dbs . c o n n e c t ( ) dbt . c o n n e c t ( )

# source # target

s e s s i o n _ s = s c o p e d _ s e s s i o n ( s e s s i o n m a k e r ( b i n d=d b s ) ) s e s s i o n _ t = s c o p e d _ s e s s i o n ( s e s s i o n m a k e r ( b i n d=d b t ) ) metadata_s = m e t a d a t a metadata_t = T h r e a d L o c a l M e t a D a t a ( ) metadata_s . b i n d = d b s metadata_t . b i n d = d b t

for

tablename

class class

Source ( Entity ) : m e t a d a t a=metadata_s , s e s s i o n=s e s s i o n _ s )

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 ,

setup_all () Target ( Entity ) : m e t a d a t a=metadata_t , s e s s i o n=s e s s i o n _ t ) column h a s _ f i e l d ( column . name , r e q u i r e d= ) s e t u p _ a l l ( 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.

PYTHON AKSES DATABASE

78

Database PostgreSQL MySQL SQLite Interbase MS SQL

Paket python-psycopg2 python-mysqldb python-sqlite2 python-kinterbasdb python-pymssql

Tabel 8.1: Driver database

37 38 39 40

for

source

t a r g e t = Target ( ) t a r g e t . from_dict ( s o u r c e . to_dict ( ) ) s e s s i o n _ t . commit ( )

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

python s q l a l c h e m y python s q l a l c h e m y doc , 1 . 2 . 1 p2 2) , 2.3.0 (>= | python p s y c o p g 2 , 2.5) | python

Suggests :

mysqldb (>= pysqlite2 1.1.7

p y t h o n (>= (>= , 1.0.1

python (>=

1)

python p y s q l i t e 1 . 1

2)

python s q l i t e (>= 3.1.2

kinterbasdb

0.3)

5 ) , python python p y m s s q l

.. Perhatikan bagian Suggests.

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

Link inet inet6

encap : L o c a l addr :

Loopback Mask : 2 5 5 . 0 . 0 . 0 Scope : Host M T U: 1 6 4 3 6 Metric : 1

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

RX p a c k e t s : 5 6 2 frame : 0 TX p a c k e t s : 5 6 2 carrier :0 c o l l i s i o n s :0 ( 5 4 0 . 8 KB) Link inet inet6

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

encap : Ethernet addr : 1 9 2 . 1 6 8 . 1 . 4 addr :

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

import import def for

Listing 9.1: netdev.py commands re

netdev ( ) : r = {} line () : line

in
=

commands . g e t o u t p u t ( ' i f c o n f i g ' ) . s p l i t l i n e s line . strip () encap ( . * ) ' ) . s e a r c h (

match = r e . c o m p i l e ( ' ( . * ) L i n k

if

line ) match : d e v i c e = match . g r o u p ( 1 ) . s t r i p ( ) r [ device ] = {} ( . * ) ' ) . search ( l i n e match = r e . c o m p i l e ( ' HWaddr

match = r e . c o m p i l e ( ' i n e t

if continue

match : r [ d e v i c e ] [ ' addr ' ] = match . g r o u p ( 1 ) addr : ( . * ) ' ) . s e a r c h ( l i n e )

BAB 9.

LINTAS SISTEM

81

17 18 19 20 21 22 23 24 25

if return if for
dev

match : r [ device ] [ ' ip ' ] r = match . g r o u p ( 1 ) . s p l i t ( ) [ 0 ]

__name__ ==

'__main__ ' : nd :

nd = n e t d e v ( )

in print

dev ,

nd [ d e v ]

Jalankan.

1 2 3 4

$ lo

python { ' ip ' : :99 '}

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

{ ' ip ' : { ' ip ' :

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

'

Lalu apa yang dilakukan oleh pelanggan Anda tadi ?

Ia perlu mengubah Biasanya

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

Database sebagai perantara bisa jadi ditolak dengan berbagai alasan.

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

XMLRPC bisa juga disebut sebagai

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

karena dokumen XML ter-

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

from import class

Listing 9.2: serverbank.py

SimpleXMLRPCServer sqlalchemy as sa

import

SimpleXMLRPCServer

S e r v e r ( SimpleXMLRPCServer ) :

a l l o w _ r e u s e _ a d d r e s s = True verify_request ( self , request , client_address ) :

def

print return

server . ip_client = client_address [ 0 ] server . ip_client server . ip_client

192.168.0.1 ' , ' 192.168.1.5 ' ]

in

[ ' 127.0.0.1 ' , '

def

return

r e s p ( code ,

msg ) : code , msg }

{ ' kode ' : ' pesan ' :

class def

Agent :

def

print try except return return


: \

_dispatch ( s e l f , method ,

method ,

params =() ) :

params ' e x p o r t _ ' + method ) tidak dikenal ' %

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 )

f u n c ( * params ) p) : ket , nominal , saldo " +

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 ' ] ,

" + \ " + \

"AND t g l : : d a t e BETWEEN '% s ' AND '% s '

p [ ' akhir ' ] )

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

r [ ' mutasi ' ] r

= m

' p o s t g r e s q l : / / ilham : 1 2 3 4 @ l o c a l h o s t / t o t a l i n d o '

db = s a . c r e a t e _ e n g i n e ( u r l ) p o r t = 9303 server = Server (( ' 0 . 0 . 0 . 0 ' , port ) ) server . register_introspection_functions ()

print
$

s e r v e r . r e g i s t e r _ i n s t a n c e ( Agent ( ) ) 'HTTP XMLRPC s e r v e r port ' , port server . serve_forever () Jalankan.

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
:

print

tgl ,

ket , tgl ,

nominal ,

saldo

ket . l j u s t (15) ,

in

r [ ' mutasi ' ] : \

s t r ( i n t ( nominal ) ) . r j u s t ( 1 0 ) , str ( int ( saldo ) ) . rjust (10)

else
$

print

r [ ' pesan ' ]

Bukalah konsole lainnya, lalu jalankan le ini.

1 2 3

python

c l i e n t b a n k . py 3000000 3000000 2800000

2010 10 22 GAJI 11 2010 2010 10 22 BON

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

PHP sebagai XMLRPC Client


File itu berada di Lalu

Unduh xmlrpc.inc dari http://phpxmlrpc.sourceforge.net. xmlrpc-2.2.2.tar.gz. Carilah. buatlah le clientbank.php. Listing 9.4: clientbank.php

Versi lain sepertinya juga tidak masalah.

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 ' )

' akhir ' = > new

xmlrpcmsg ( ' m u t a s i ' , xmlrpc_client ( ' / ' ,

' 127.0.0.1 ' ,

array

( $params ) ) ; 9303) ;

$ r = $c >s e n d ( $ f ) ; $v = $ r >v a l u e ( ) ;

if

print else if foreach


{ $tgl $ket $saldo

( $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 ( ) ;

} } } } ?>

print str_pad str_pad str_pad else print


{

$tgl . "

( $nominal , 1 0 ) . "

( $ s a l d o , 1 0 ) . " \n" ;

$ r e s p [ ' p e s a n ' ]> s c a l a r v a l ( ) ;

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

c l i e n t _ b a n k . php 3000000 3000000 2800000

2010 10 22 GAJI 11 2010 2010 10 22 BON

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.

Buatlah le sites/all/modules/pegawai/pegawai.module berikut. Listing 9.5: pegawai.module

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

<?php function pegawai_menu ( ) $items =

$ i t e m s [ ' p e g a w a i / t r x/%/%/% ' ] ' title ' = > ' page ' page ' Transaksi ' , callback ' = > arguments ' = >

array

{ =

() ;

array

' pegawai_trx ' ,

' access ) ; return } function $p =

callback ' = >

array

(2 ,3 ,4) ,

' user_access ' ,

' type ' = > MENU_CALLBACK, $items ;

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

( ! $r [ ' mutasi ' ] ) return

drupal_set_message ( ' Tidak

} $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 > ' ;

Juga buat sites/all/modules/pegawai/pegawai.info. Listing 9.6: pegawai.info

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

Mulailah membuat direktori kerja, yaitu /usr/local/src/python-uang.

sudo

mkdir

/ u s r / l o c a l / s r c / python uang /DEBIAN

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

control postinst prerm

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

di dalam sudah terpasang

Kemudian dibuatlah versi 0.2 yang juga versi 0.2 tidak ada perubahan

1 2 3 4 5 6 7 8 9

Package : Priority : Section :

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)

Maintainer : Version : Depends : 0.1

Architecture :

python c e n t r a l all uang Format

Python V e r s i o n : Description :

Lanjutkan dengan membuat direktori modulnya.

BAB 10.

PENGEMASAN

91

1 2

$ $

cd

/ usr / l o c a l / src mkdir

sudo

uang / s i t e

p python uang / u s r / s h a r e / p y c e n t r a l / python p a c k a g e s


Semua direk-

Perhatikanlah direktori setelah /usr/local/src/python-uang. DEBIAN.

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

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 packages /

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

import from from

Listing 10.2: usr/share/pycentral/python-uang/site-packages/uang.py locale

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

ribu ( nilai , pecahan

if else return def if return

t y p e ( n i l a i ) == I n t T y p e : : l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n , nilai , True )

is

p e c a h a n=None ) : None :

pecahan = 0 p e c a h a n = DECIMAL

uang ( n i l a i , tanda tanda =

is

p e c a h a n=None ,

t a n d a=None ) :

None : l o c a l e . l o c a l e c o n v ( ) [ ' currency_symbol ' ] ribu ( nilai , pecahan ) )

'%s%s ' % ( t a n d a ,

BAB 10.

PENGEMASAN

92

26 27 28 29 30 31

if

__name__ == n = 10000.5

'__main__ ' :

print print print

uang ( n ) uang ( i n t ( n ) ) uang ( n , 3 )

Buat direktori etc.

sudo

mkdir

python uang / e t c

Selanjutnya buat python-uang/etc/uang.conf. Listing 10.3: etc/uang.conf

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

#!/ b i n / s h pycentral pkginstall python uang

Juga python-uang/DEBIAN/prerm. Listing 10.6: DEBIAN/prerm

1 2

#!/ b i n / s h pycentral pkgremove python uang

Buat keduanya executable.

1 2

$ $

sudo sudo

chmod chmod

755 755

python uang /DEBIAN/ p o s t i n s t python uang /DEBIAN/ prerm

Pastikan semua le dimiliki root.

sudo

chown

root . root

python uang

Lihat lagi apa yang sudah dibuat.

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

python uang field

dpkgdeb : dpkgdeb : dpkgdeb : control

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

uang_0 . 1 _ a l l . deb ' . peringatan : file (s) ignoring warnings

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

S e d a n g membuka _ a l l . deb ) Sedang Processing menyetel

triggers

Untuk menghindari kerancuan karena ada dua modul uang yang terpasang, sebaiknya buang yang ada di /usr/local.

s u d o mv / 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 /tmp Lalu ujilah di modus interaktif.

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

Python [GCC Type

information . > > > from

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

remove daftar yang

python uang paket . . . Selesai Selesai

Sedang Membaca Paket 0

Membangun pohon berikut

ketergantungan tersedia . . .

informasi

akan DIHAPUS : 0 baru terinstal , 1 akan dihapus akan dan 192

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

Letakkan paketnya di tempat yang sudah ditentukan web server Apache.

1 2

$ $

sudo sudo

mkdir cp

/ v a r /www/ deb / v a r /www

/ u s r / l o c a l / s r c / python uang_0 . 1 _ a l l . deb

/ 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

Setiap ada paket baru jalankan le ini.

1 2

$ $

cd

/ v a r /www/ deb sh update l i s t . s h

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

Listing 10.8: /etc/apt/sources.list.d/custom.list

deb

h t t p : / / 1 9 2 . 1 6 8 . 0 . 1 / deb

./

Perbaharui daftar paket.

sudo

a p t g e t

update

Pasang paket yang dimaksud.

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

Buat le conf. Listing 10.9: remaster/conf

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"

Sesuaikanlah ketiganya. Kemudian buat le 1-extract.sh: Listing 10.10: remaster/1-extract.sh

1 2 3 4 5 6 7 8

./ conf

mkdir mount rsync mount cp

p /tmp/ cdrom /tmp/ r o o t o l o o p $SOURCE /tmp/ cdrom e x c l u d e ' f i l e s y s t e m . s q u a s h f s o


loop

'

/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

Lalu buat le 2-chroot.sh. Listing 10.11: remaster/2-chroot.sh

1 2 3

cp

/ etc / resolv . conf root root mount mount

root / etc proc sysfs none none / proc / sys

chroot chroot

t t

BAB 10.

PENGEMASAN

96

4 5 6 7 8 9 10 11 12 13

chroot chroot chroot chroot chroot rm

root root root root root

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

cdrom / p r o g r a m s root dpkgq u e r y

chmod + w cdrom / c a s p e r / f i l e s y s t e m . m a n i f e s t chroot cp

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 /

${

V e r s i o n }\ n ' > cdrom / c a s p e r / f i l e s y s t e m . m a n i f e s t cdrom / c a s p e r / f i l e s y s t e m . m a n i f e s t f i l e s y s t e m . m a n i f e s t d e s k t o p ie ' / u b i q u i t y /d ' cdrom / c a s p e r / f i l e s y s t e m . m a n i f e s t

sed

desktop

Terakhir buat le 3-pack.sh. Listing 10.12: remaster/3-pack.sh

1 2 3 4 5 6 7

. rm

./ conf

cdrom / c a s p e r / f i l e s y s t e m . s q u a s h f s root cdrom / c a s p e r / f i l e s y s t e m . s q u a s h f s

awal =` date `

cd

mksquashfs cdrom find .

mkisofs

8 9 10 11

echo echo echo echo


$

t y p e f p r i n t 0 | x a r g s 0 md5sum > md5sum . t x t r V " $ {LABEL} " c a c h e i n o d e s J l b i s o l i n u x / i s o l i n u x . b i n c i s o l i n u x / b o o t . noemulb o o t b o o t l o a d s i z e 4 b o o t i n f o t a b l e o . . / $TARGET .

cat

" Awal " Akhir " Source " Target

$awal " " ` date ` " ` ls " ` ls

l l

$SOURCE

. . / $TARGET

awk awk
|

'{ p r i n t

$5 } ' ` $5 } ' `

'{ p r i n t

Jalankan script pertama.

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.

r o o t @ l a p t o p :/# Di sini Anda bisa melakukan perubahan sebagaimana biasa. Contoh:

1 2 3 4

r o o t @ l a p t o p :/# r o o t @ l a p t o p :/# r o o t @ l a p t o p :/# r o o t @ l a p t o p :/# server

cd

/ e t c / apt / s o u r c e s . l i s t . d http : / / debian . rab . co . i d / rab . l i s t update install i n t e r n e t s h a r i n g dhcp3

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.

s u d o 3 p a c k . s h Tunggu sekitar 5 menit.

1 2 3

Parallel Creating

mksquashfs : 4.0 block

Using on

processors

filesystem size

cdrom / c a s p e r / f i l e s y s t e m . ] 15063/84258 17%

squashfs ,

131072.

[ = = = = = = = = = = = = = = = = = = = = = = = = = = |

Terakhir script akan memberikan informasi ukuran iso yang dihasilkan berikut iso aslinya.

1 2 3 4 5

357433 Awal Akhir Source Target

extents Sen Agu Sen Agu 2

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

Setelah selesai jalankan lagi script ketiga:

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

Juga dokumentasinya, meski tidak wajib.

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

import from from class def

Listing 11.1: hello.py sys

PyQt4

PyQt4 . QtGui

import

import

Qt

FormUtama ( QMainWindow ) : __init__ ( s e l f ) : QMainWindow . __init__ ( s e l f ) s e l f . setWindowTitle ( ' H e l l o world ' )

98

BAB 11.

GRAPHICAL USER INTERFACE

99

Gambar 11.1: Hello world

11 12 13 14

app = Qt . Q A p p l i c a t i o n ( s y s . a r g v ) fm = FormUtama ( ) fm . show ( ) app . exec_ ( ) Butuh tampilan penuh ? Gantilah

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.

GRAPHICAL USER INTERFACE

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

Mari kita buat form yang lebih lengkap.

pegawai, terinspirasi dari tabel pegawai yang pernah kita buat. Namun disini belum menggunakan database, semuanya tersimpan sementara saja.

BAB 11.

GRAPHICAL USER INTERFACE

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

import from from from class def

Listing 11.2: gui/pegawai.py sys

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 )

#s e l f . labelTglLahir . setText ( ' Tgl Lahir ')

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 . editNama . resize (200 ,30)


s e l f . l a b e l N a m a . move ( 1 0 , 1 0 ) s e l f . l a b e l A l a m a t . move ( 1 0 , 4 0 )

s e l f . l i s t P e g a w a i . setColumnWidth ( 1 , 1 5 0 ) s e l f . editAlamat . r e s i z e (200 ,100) s e l f . listPegawai . r e s i z e (500 ,200)

s e l f . l a b e l T g l L a h i r . move ( 1 0 , 1 4 0 )

#s e l f . editNama . move(100 ,10)

s e l f . l a b e l J e n i s . move ( 1 0 , 1 7 0 ) s e l f . editNama . s e t G e o m e t r y ( 1 0 0 , 1 0 , s e l f . e d i t A l a m a t . move ( 1 0 0 , 4 0 ) s e l f . e d i t T g l L a h i r . move ( 1 0 0 , 1 4 0 ) 200 ,30)

BAB 11.

GRAPHICAL USER INTERFACE

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

s e l f . e d i t J e n i s . move ( 1 0 0 , 1 7 0 ) s e l f . b u t t o n S i m p a n . move ( 1 0 0 , 2 0 0 ) s e l f . l i s t P e g a w a i . move ( 1 0 , 3 0 0 ) s e l f . c o n n e c t ( s e l f . buttonSimpan , ') , s e l f . simpan ) event ) : SIGNAL ( ' c l i c k e d ( )

def

closeEvent ( s e l f ,

for

s =

' ' row

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 =

s t r ( s e l f . l i s t P e g a w a i . i t e m ( row , c o l ) . text () ) ' , '

s += r . s t r i p ( ' , ' ) + f . write ( s ) f . close ()

#r = r + v + ' , '

r += v +

' \n '

f = o p e n ( ' p e g a w a i . c s v ' , 'w ' )

# 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 ( )

app = Qt . Q A p p l i c a t i o n ( s y s . a r g v ) fm = FormUtama ( ) fm . show ( ) app . exec_ ( )

BAB 11.

GRAPHICAL USER INTERFACE

103

Gambar 11.2: Daftar Pegawai

Bagaimana, panjang bukan ? Ya, contoh kali ini menyertakan class yang kerap digunakan. ulasan masing-masing class tersebut. Berikut ini

QLabel QLineEdit QTextEdit QDateEdit QComboBox QPushButton

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.

GRAPHICAL USER INTERFACE

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

1 Jeruk 2 Mangga 3 Pisang

34 7

9000 8000 10000

tetap

Pembuat le itu memastikan data barang tersimpan dengan .

Kolom kode barang 3 karakter, nama barang 10 karakter, stok 2 ka-

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.

OBJECT ORIENTED PROGRAMMING

106

Mulailah membuat script barang1.py.

1 2 3 4 5 6 7 8 9 10 11

import for

Listing 12.2: barang1.py sys

f i l e n a m e = sys . argv [ 1 ] f = open ( f i l e n a m e ) line kode = nama = stok =

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 ()

print

harga =

[ kode ,

Jalankan.

1 2 3 4

python

b a r a n g 1 . py '34 ' , ' ' , '7 ' ,

barang . t x t '9000 '] '8000 '] '10000 ']

[ '1 ' , [ '2 ' , [ '3 ' ,

' Jeruk ' , ' Mangga ' , ' Pisang ' ,

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

Listing 12.3: barang2.py barang ( f i l e n a m e ) : f = open ( f i l e n a m e ) rows =

for

[]

line

kode = nama = stok =

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 ()

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.

OBJECT ORIENTED PROGRAMMING

107

16 17 18 19

import for in print


sys row Jalankan. $ python [ '1 ' , [ '2 ' , [ '3 ' , ' Jeruk ' ,

f i l e n a m e = sys . argv [ 1 ] barang ( f i l e n a m e ) : row

1 2 3 4

b a r a n g 2 . py '34 ' , ' ' , '7 ' ,

barang . t x t '9000 '] '8000 '] '10000 ']

' Mangga ' , ' Pisang ' ,

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

kolom memiliki lebar tertentu


berbeda.

. Kalau barang.txt menggunakan lebar kolom

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

Listing 12.4: xtable.py f i x t a b l e ( filename , f = open ( f i l e n a m e ) rows = widths ) :

for

[]

line

awal = 0 row =

in

f . readlines () :

for

[]

width field

a k h i r = awal + width = l i n e [ awal : a k h i r ] . s t r i p ( ) row . append ( f i e l d ) awal = a k h i r

in

widths :

r o w s . append ( row )

return if

f . close () rows

__name__ ==

'__main__ ' :

BAB 12.

OBJECT ORIENTED PROGRAMMING

108

18 19 20 21

import for in print


sys row Jalankan. $ python [ '1 ' , [ '2 ' , [ '3 ' , ' Jeruk ' ,

f i l e n a m e = sys . argv [ 1 ] f i x t a b l e ( filename , row [3 ,10 ,2 ,8]) :

1 2 3 4

f i x t a b l e . py '34 ' , ' ' , '7 ' ,

barang . t x t '9000 '] '8000 '] '10000 '] Anda bisa uji dengan membuat pega-

' Mangga ' , ' Pisang ' ,

Perhatikan lagi, hasil masih sama. wai.txt.

Listing 12.5: pegawai.txt

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

from import for


$

fixtable sys

import

Listing 12.6: xpegawai.py fixtable

f i l e n a m e = sys . argv [ 1 ] row

in print

f i x t a b l e ( filename , row

[2 ,22 ,10 ,1]) :

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 ' ,

Fungsi xtable teruji.

12.3

Tingkat Kerumitan
Kini Anda membutuhkan hasil yang

Kompleksitas masalah kian bertambah.

memperhatikan tipe data

. Perhatikan lagi barang.txt dimana:

BAB 12.

OBJECT ORIENTED PROGRAMMING

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
[]

Listing 12.7: FixTableType.py IntType , StringType

f i x t a b l e ( filename , f = open ( f i l e n a m e ) rows =

ftypes ) :

for

line

awal = 0 row =

in

f . readlines () :

for

[] ftype =

width ,

a k h i r = awal + width

in

ftypes :

if not elif return if


f . close () rows

field

l i n e [ awal : a k h i r ] . s t r i p ( ) field : = None = int ( f i e l d )

field field

f t y p e == I n t T y p e :

row . append ( f i e l d ) awal = a k h i r r o w s . append ( row )

__name__ ==

import
fields

'__main__ ' :

sys = [

f i l e n a m e = sys . argv [ 1 ] [ 3 , IntType ] , [ 1 0 , StringType ] , [ 2 , IntType ] , [ 8 , IntType ] ]

BAB 12.

OBJECT ORIENTED PROGRAMMING

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

F i x T a b l e T y p e . py 34 , 7, 9000] 8000] 10000] None ,

barang . t x t

' Jeruk ' , ' Mangga ' , ' Pisang ' ,

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

Listing 13.1: testthread.py Thread

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

import import from

time os

glob

import

glob

class def def

K i r i m ( Thread ) : __init__ ( s e l f , s e l f . kabar = k Thread . __init__ ( s e l f ) k) : s e l f . k e r j a = True

print while if not continue del #s e l f . kabar = s e l f . kabar [ 1 : ] print


dimulai ' s e l f . kerja : time . s l e e p ( 1 ) s e l f . kabar : hasil = s e l f . kabar [ 0 ] s e l f . kabar [ 0 ] :%S ' ) , hasil )

run ( s e l f ) :

t i m e . s t r f t i m e ( '% H:%M:%S ' ) ,

' Kerja

sampingan

'%s KABARI %s ' % ( t i m e . s t r f t i m e ( '% H:%M

def

print

join ( self ) : ' Kerja sampingan berakhir ' s e l f . kerja = False

Thread . j o i n ( s e l f )

job_dir = kabar = []

' /tmp/ j o b '

sampingan = Kirim ( kabar ) sampingan . s t a r t ( )

print while if not continue if not continue


' True :

t i m e . s t r f t i m e ( '% H:%M:%S ' ) ,

' Pekerjaan

utama

dimulai

time . s l e e p ( 1 ) os . path . e x i s t s ( j ob _d ir ) :

f i l e n a m e s = g l o b ( '%s / * ' % j o b _ d i r ) filenames :

filename = filenames [ 0 ]

BAB 13.

KERJA SAMPINGAN

113

45 46 47 48 49 50 51 52 53

f = open ( f i l e n a m e ) job = f . read ( ) f . close ()

print print

o s . remove ( f i l e n a m e ) '%s KERJAKAN %s ' % ( t i m e . s t r f t i m e ( '% H:%M:%S ' ) , job )

k a b a r . append ( j o b ) ' Pekerjaan utama berakhir '

sampingan . j o i n ( ) Jalankan.

1 2 3

python

t e s t t h r e a d . py Pekerjaan Kerja utama dimulai dimulai Buka sampingan

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

Lalu buatlah le apa saja.

echo

h e l l o > /tmp/ j o b / h e l l o . t x t

Kemudian lihat konsole testthread.py tadi.

1 2

0 4 : 5 6 : 5 6 KERJAKAN h e l l o 04:56:57 KABARI hello

Pointer
Perlu diperhatikan variabel kabar yang bertipe list (baris 30). Ini adalah

abel bersama
1
adalah

, artinya dapat diolah baik oleh thread 1 maupun thread 2. Di

vari-

sini berlaku apa yang disebut pointer yang artinya alokasi memori pada baris 30 dengan baris 10 s e l f . kabar = k

sama

. Perhatikan juga penghapusan antrian pertama di baris 20

del

s e l f . kabar [ 0 ]

dimana pada program biasa bisa saja Anda membuatnya menjadi

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 =

' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t :5432/ t o t a l i n d o '

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

05:12:20 ,883 05:12:20 ,886

im / r e s u l t /

114

BAB 14.

SMS GATEWAY

115

2011 01 18 on port

05:12:20 ,928 9317

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

grep 0:06 0:01

modem / u s r / b i n / python python / u s r / b i n /modem h o t p l u g / u s r / b i n /modem / d e v / ttyUSB0

? 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

25.972197] 1 6 5 5 0A 25.973141] 1 6 5 5 0A 42.442528] is is is is a a a a 42.442750] 42.538359] 42.538574]

serial8250 : 00:07: ttyS0

I /O 0 x 3 f 8 at at at at

( i r q = 4)

0000:05:01.0: 0000:05:01.0: 0000:05:02.0: 0000:05:02.0:

ttyS4 ttyS5 ttyS6 ttyS7

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

( i r q = 17) ( i r q = 17) ( i r q = 19) ( i r q = 19)

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

Lalu restart semua modem:

BAB 14.

SMS GATEWAY

116

sudo

/ e t c / i n i t . d /modem

restart

Tunggu sekitar 30 detik, lalu jalankan

1 2

ps

ax ?

grep

modem Rl 10:29 python / u s r / b i n /modem / d e v /

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

510012541218911. log Selanjutnya mulailah memantau:

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

Password psql SSL Type

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!

Mari mulai mengirim SMS ke handphone Anda, masih di psql.

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

t o t a l i n d o= > SELECT i d , id | status |

status ,

pengirim , |

penerima , |

pesan 1;

totalindo > F R O M im . s e l e s a i pesan

ORDER BY 1 DESC LIMIT penerima

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

t o t a l i n d o= > SELECT i d , id | kirim |

kirim ,

pengirim , |

penerima , |

pesan pesan

totalindo > F R O M im . a n t r i a n ; pengirim penerima

++++
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

Instant Messenger Gateway

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

Gambar 14.1: Alur IM gateway

BAB 14.

SMS GATEWAY

120

sudo

a p t g e t

install

im ym

Kemudian sesuaikan /etc/im/ym/cong.py, seperti contoh berikut:

users = { ' inforab ' :

' 1234 ' }

dimana inforab adalah Yahoo! account dan 1234 adalah passwordnya. Setelah disimpan tunggu satu menit dan periksa keberadaannya.

1 2

ps

ax ?

g r e p ym Sl 24:44 python / u s r / b i n /ym inforab

2339

Log-nya juga tersedia.

1 2

sudo

tail

/ v a r / l o g /ym/ i n f o r a b . l o g INFO Startup { ' status ' : 0}

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

{ ' tgl_operator ' : ' hello world ' , '

'2011 02 25 pengirim ' :

15:10:43+7 ' , ' info_rab ' }

' pesan ' :

dan lihat juga tabel im.antrian.

1 2 3 4 5 6

t o t a l i n d o= > SELECT i d , pesan

kirim ,

jalur ,

pengirim ,

penerima ,

totalindo > F R O M im . a n t r i a n ; id | kirim | jalur | pengirim | penerima | pesan

+++++
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

Lalu sesuaikan /etc/im/xmpp/cong.py.

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 '

' i n f o @ j a b b e r . rab . co . i d ' :

14.4

Broadcast

Anda telah memiliki banyak pelanggan dan ingin mengirim pesan yang sama ke mereka. Dengan mudah lakukan query berikut.

1 2

INSERT INTO im . a n t r i a n ( p e n e r i m a , SELECT no_hp , ' Selamat

pesan )

transaksi ' F R O M pelanggan ;

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

t o t a l i n d o= > SELECT nextval

n e x t v a l ( ' im . b r o a d c a s t _ i d _ s e q ' ) ;

8 (1 row ) Kemudian tambahkan pesannya.

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 ;

Dimana jalur 5 adalah ym.

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

Anda mungkin juga menyukai