Database PostgreSQL, Pemrograman Python, dan SMS Gateway
RAB Linux Indonesia
©2010
Daftar Isi
1 Alasan 2 Pemasangan 3 Hello World! 3.1
Variabel dan Tipe Datanya
3.2
Kondisi
3.3
Perulangan
4 Tipe Data
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
11 13
16
4.1
String
4.2
Bilangan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
4.2.1
Konversi Tipe Data
18
4.2.2
Formatting
. . . . . . . . . . . . . . . . . . . . . . . . . .
18
4.2.3
Kalkulator
. . . . . . . . . . . . . . . . . . . . . . . . . .
19
4.3
List
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 5 8
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
19
4.3.1
Pemenggalan
20
4.3.2
Keberadaan Elemen
. . . . . . . . . . . . . . . . . . . . .
21
4.3.3
Mengubah Elemen . . . . . . . . . . . . . . . . . . . . . .
24
4.4
Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
4.5
Waktu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
5 Modularitas
30
5.1
Membuat Fungsi
. . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2
Membuat Modul
. . . . . . . . . . . . . . . . . . . . . . . . . . .
32
5.3
Search Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
6 Fungsi 6.1
Memanggil Dirinya Sendiri
6.2
Format Uang
7 Database 7.1
Tabel
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.1.1
Sequence
. . . . . . . . . . . . . . . . . . . . . . . . . . .
7.1.2
Mengubah Struktur
. . . . . . . . . . . . . . . . . . . . .
1
31
36 36
36
41 43
45 48
2
DAFTAR ISI
7.2
View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
50
7.3
Backup dan Restore
. . . . . . . . . . . . . . . . . . . . . . . . .
52
. . . . . . . . . . . . . . . . . . . . . . . . . . .
53
7.3.1 7.4
Encoding
Fungsi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
7.4.1
PL/pgSQL
. . . . . . . . . . . . . . . . . . . . . . . . . .
55
7.4.2
PL/Python
. . . . . . . . . . . . . . . . . . . . . . . . . .
60
8 Python Akses Database 8.1
Active Record . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2
Auto Commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.3
8.4
Synchronizer
63
70
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
8.3.1
Aspek Keamanan . . . . . . . . . . . . . . . . . . . . . . .
73
8.3.2
Tambah, Perbaharui, Hapus . . . . . . . . . . . . . . . . .
75
Migrasi dari Database Lain
. . . . . . . . . . . . . . . . . . . . .
9 Lintas Sistem 9.1
Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.2
File
9.3
Database
9.4
62
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
77
79 79
81
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
82
XMLRPC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
83
9.4.1
PHP sebagai XMLRPC Client
. . . . . . . . . . . . . . .
86
9.4.2
Drupal . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
87
10 Pengemasan
10.1 Paket Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Debian Repository
89 89
. . . . . . . . . . . . . . . . . . . . . . . . . .
94
10.3 Remastering Ubuntu . . . . . . . . . . . . . . . . . . . . . . . . .
95
11 Graphical User Interface
11.1 Orientasi Objek . . . . . . . . . . . . . . . . . . . . . . . . . . . .
98 99
11.2 Daftar Pegawai . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
12 Object Oriented Programming
105
12.1 Lebih Terstruktur . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 12.2 Lebih Umum
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
12.3 Tingkat Kerumitan . . . . . . . . . . . . . . . . . . . . . . . . . . 108
13 Kerja Sampingan 14 SMS Gateway
111 114
14.1 Pemasangan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 14.1.1 IMEI Chip 14.1.2 Database
. . . . . . . . . . . . . . . . . . . . . . . . . . 116 . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
14.2 Hello world! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 14.3 Instant Messenger Gateway 14.3.1 Yahoo! Messenger
. . . . . . . . . . . . . . . . . . . . . 118
. . . . . . . . . . . . . . . . . . . . . . 118
14.3.2 XMPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
DAFTAR ISI
3
14.4 Broadcast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Bab 1 Alasan
Mengapa Python merupakan bahasa yang tepat untuk pembuatan berbagai aplikasi ? Berikut ini alasannya.
Multiplatform
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.
Mudah
Python tergolong scripting, artinya Anda cukup tulis source-nya di
text editor biasa, lalu jalankan.
Hemat
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.
PostgreSQL tergolong lengkap dan mudah dikembangkan.
Memili-
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).
4
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:
1
$
sudo
a p t −g e t
update
Pasang PostgreSQL:
1
$
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:
1
$
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.
Paket ini memuat psql yang digunakan untuk terhubung ke Pos-
tgreSQL server, secara command line. Ada baiknya Anda juga memasang versi web-nya:
1
$
sudo
a p t −g e t
install
phppgadmin
Kadang web server Apache tidak otomatis hidup:
1
$
sudo
/ e t c / i n i t . d/ apache2
start
Secara default Apache hidup otomatis saat komputer boot. Lalu buatlah symlink phppgadmin:
1
$
sudo
ln
−s
/ u s r / s h a r e / phppgadmin
/ v a r /www/
Kemudian di browser (sebaiknya Firefox) buka url:
5
BAB 2.
6
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.
1
$
sudo
a p t −g e t
install
vim
Kemudian sesuaikan /etc/vim/vimrc,
1
$
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
" Vim5 and
later
versions
Uncommenting
2
"
3
syntax
line
enables
the
support
syntax
highlighting .
next
syntax
highlighting
by
default .
on
Saat Anda membuka le untuk keduakalinya, maka kursor langsung menuju ke lokasi sebelumnya.
1
" Uncomment position
2
"
3
if
4
reopening
the
to
h a v e Vim jump
to
the
last
a
file
h a s ( " autocmd " ) au
*
BufReadPost l i n e (" $ ")
5
following
when
|
if
l i n e ( " ' \ " " ) > 0 && l i n e ( " ' \ " " ) <=
exe
" normal
g '\""
|
endif
endif Menjorok otomatis (auto indent), dan penekanan tombol TAB diganti dengan SPACE 4 kali:
1
" Uncomment rules
2
"
detected filetype
3
"
4
if
the
following
according
to
filetype .
to
h a v e Vim
load
indentation
the
Per
default
D e b i a n Vim
only
load
specific
plugins . h a s ( " autocmd " )
5
filetype
6
set
smartindent
7
set
expandtab
8
set
t a b s t o p =4
9
set
s o f t t a b s t o p =4
10
set
s h i f t w i d t h =4
11
endif
indent
on
Pencarian mengabaikan huruf besar atau kecil (incasesensitive).
1
set
ignorecase
" Do
case
insensitive
matching
BAB 2.
7
PEMASANGAN
Bila kata yang dicari mengandung huruf besar dan huruf kecil, maka pencarian memperhatikan huruf besar atau kecil (casesensitive).
1
set
smartcase
" Do s m a r t
case
matching
Aktifkan kedua opsi pencarian di atas, maka pencarian di vim ( menggunakan perintah slash / ) semakin mudah.
Bab 3 Hello World!
Anda bisa memulai Python dalam modus interaktif. Nanti kalau source sudah mulai panjang kita simpan dalam le. Modus interaktif dijalankan di konsole, lalu ketik:
1
$
python maka sambutannya seperti ini:
1
Python
2
[GCC
4.4.3]
3
Type
" help " ,
4
information .
5
2.6.5
( r265 :79063 , on
Apr
16
2010 ,
13:57:41)
linux2
" copyright " ,
" credits "
or
"license"
for
more
>>> Mulailah dengan yang sederhana, menampilkan sebuah pesan menggunakan perintah print.
1 2
>>> p r i n t 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
>>> p r i n t
2
HELLO WORLD!
3
>>> p r i n t
4
hello
' Hello ' Hello
World ! ' . u p p e r ( ) World ! ' . l o w e r ( )
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().
Dengan demikian 'Hello World!'
bukan
sekedar string, tapi disebut OBJEK string. Inilah ciri objek, memuat data dan fungsi.
8
BAB 3.
9
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
>>> p r i n t Hari
" Hari
Jum ' a t "
Jum ' a t
Bila string lebih dari satu baris Anda bisa gunakan kutip tiga kali:
1 2 3
>>> p r i n t
" " "Nama :
...
Alamat :
...
Hobi :
4
Nama :
5
Alamat :
6
Hobi :
Bummi Dwi
Putera
B og o r
Menggambar " " "
Bummi Dwi
Putera
B o go r
Menggambar
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.
Anda bisa gunakan text editor favorit seperti gedit,
kate, nano, joe, atau vi. Listing 3.1: pegawai.py
1
nama = r a w _ i n p u t ( ' Nama :
2
a l a m a t = r a w _ i n p u t ( ' Alamat :
3
h o b i = r a w _ i n p u t ( ' Hobi :
4 5 6
print print print
' Nama :
' ,
' Alamat : ' Hobi :
') ')
')
nama ' ,
' ,
alamat
hobi
Setelah disimpan jalankan:
1
$
python
p e g a w a i . py
Lalu masukkan data seperti yang diminta:
1
Nama :
2
Alamat :
3
Hobi :
Bummi Dwi
Putera
B o go r
Menggambar
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.
10
HELLO WORLD!
1
>>> nama = r a w _ i n p u t ( ' Nama :
2
Nama :
3
>>> p r i n t
4
')
Bummi t y p e ( nama )
' s t r '>
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
>>> a = 3
2
>>> b = 5
3
>>> p r i n t
4
a + b
8
5
>>> p r i n t
6
type ( a )
' i n t '>
Variabel a dan b bertipe integer alias bilangan bulat.
Berikut ini untuk
bilangan pecahan (oat).
1
>>> c = 7 . 2
2
>>> p r i n t
3
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.
Kita tidak
diperkenankan menambahkan string dengan integer.
1
>>> nama =
2
>>> umur = 24
3
>>> p r i n t
4
Traceback
5 6
File
' Bummi '
nama + ( most
'
"< s t d i n >" ,
TypeError :
usia
recent line
cannot
' + umur + call 1,
'
tahun '
last ) :
i n <module>
concatenate
' str '
and
' int '
objects
Untuk mengatasinya jadikan variabel umur menjadi string:
1
>>> p r i n t
2
Bummi
usia
nama + 24
'
usia
' +
s t r ( umur ) +
'
tahun '
tahun
Atau dengan cara lain yg lebih mudah dibaca:
1
>>> p r i n t
2
Bummi
usia
'% s 24
u s i a %s
tahun ' % ( nama ,
umur )
tahun
Meski string tidak bisa ditambah dengan integer, namun string bisa dikalikan dengan integer:
1 2
>>> p r i n t
' 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.
11
HELLO WORLD!
1
nama = r a w _ i n p u t ( ' Nama :
2
a l a m a t = r a w _ i n p u t ( ' Alamat :
3
h o b i = r a w _ i n p u t ( ' Hobi :
4
print
' Nama :
5
print
' Alamat :
6
print
',
' Hobi :
') ')
')
nama ',
alamat
',
hobi
Kemudian jalankan:
1
$
python
p e g a w a i . py
hasilnya Python protes karena ada indent (menjorok masuk) yang tidak diperkenankan:
1
$
2
python File
3
print
4 5
p e g a w a i . py
" p e g a w a i . py " , ' Hobi :
',
line
6
hobi
^ IndentationError :
unexpected
indent
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.
Baris yang memiliki
indent berarti dianggap bagian dari sub-blok seperti dalam looping (for) atau kondisi (if ).
1
nama = r a w _ i n p u t ( ' Nama :
2
a l a m a t = r a w _ i n p u t ( ' Alamat :
3
h o b i = r a w _ i n p u t ( ' Hobi :
4
print
' Nama :
5
print
' Alamat :
6
if
7
',
') ')
')
nama ',
alamat
',
hobi
hobi : print
' Hobi :
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
if
2
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.
12
HELLO WORLD!
Tipe
False
True (Contoh)
String
'abc'
Integer
0
1
Float
0
1.2
List
[]
[10,20,30]
Dictionary
{}
{'nama': 'Bummi', 'alamat': 'Bogor'}
Objek
None
True
Tabel 3.1: Kondisi False di berbagai tipe data
1
>>> h o b i =
2
>>> h o b i
3 4 5
' Menggambar '
!=
''
>>> h o b i ==
''
True False Lalu cobalah variabel hobi kosong.
1
>>> h o b i =
2
>>> h o b i
3 4 5
''
!=
''
>>> h o b i ==
''
False True Pahami baik-baik perbedaan keduanya. Perhatikan juga penggunaan karakter samadengan dua kali ( == ) yang berarti operasi logika (boolean operation).
1
>>> k o s o n g = h o b i ==
2
>>> p r i n t
3
''
kosong
True 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
nama = r a w _ i n p u t ( ' Nama :
2
a l a m a t = r a w _ i n p u t ( ' Alamat :
3
h o b i = r a w _ i n p u t ( ' Hobi :
4 5 6 7 8 9
print print if print else print
' Nama :
' ,
' Alamat :
') ')
')
nama ' ,
alamat
' ,
hobi
hobi : ' Hobi :
:
' Sebaiknya
hobi
diisi . '
else digunakan untuk kondisi sebaliknya (False). tambahan ?
Bagaimana jika kondisi
Misalkan jika hobinya menggambar maka pesan untuk hadir di
hari Sabtu ditampilkan.
BAB 3.
13
HELLO WORLD!
Listing 3.3: pegawai2.py
1
nama = r a w _ i n p u t ( ' Nama :
2
a l a m a t = r a w _ i n p u t ( ' Alamat :
3
h o b i = r a w _ i n p u t ( ' Hobi :
4 5 6 7 8 9 10 11
print print if print elif print else print
' Nama :
' ,
' Alamat :
') ')
')
nama ' ,
alamat
h o b i . u p p e r ( ) == ' Datanglah
'MENGGAMBAR' : di
pelatihan
gambar
setiap
Sabtu . '
hobi : ' Hobi :
' ,
hobi
:
' Sebaiknya
hobi
diisi . '
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
Listing 3.4: pegawai3.py
while if not break True :
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 :
print print if print else print
' Nama :
' ,
' Alamat :
')
')
nama ' ,
alamat
' ,
hobi
hobi : ' Hobi :
:
' Sebaiknya
hobi
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.
14
HELLO WORLD!
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
lanjut =
'Y '
while if not print continue if not print continue print print if print else print lanjut
!=
'S ' :
nama = r a w _ i n p u t ( ' Nama :
')
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
h o b i = r a w _ i n p u t ( ' Hobi : ' Nama :
' ,
' Alamat :
diisi . ' ')
nama ' ,
alamat
' ,
hobi
hobi :
' Hobi :
:
' Sebaiknya
hobi
diisi . '
l a n j u t = r a w _ i n p u t ( ' Tekan
"S"
jika
selesai :
' ) . upper ( )
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
for
2
i
in
[1 ,2 ,3 ,4 ,5]:
print
i
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
i
in
print
range (5) : i
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
i
in
print
range ( 1 , 6 ) : i
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.
1
>>> nama =
' Agus '
Atau bisa juga menggunakan kutip ganda.
1
>>> nama = Agus Jika Anda butuh kutip tunggal di dalam string, siasatilah.
1
>>> h a r i = Jum ' a t Masih belum yakin apa tipe data variabel nama ? Gunakan type().
1
>>> t y p e ( nama )
2
' 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
>>> p r i n t
2
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
>>> p r i n t
2
Agus
lahir
'% s
lahir
hari
h a r i %s ' % ( nama ,
Jum ' a t
16
hari )
BAB 4.
17
TIPE DATA
Lalu bagaimana menampilkan % itu sendiri di dalam formatting ? Sebutkanlah dua kali.
1 2
>>> p r i n t
' Keuntungan
Keuntungan
4.2
bulan
ini
bulan
ini
meningkat
m e n i n g k a t %d %%' % ( 1 0 ) 10 %
Bilangan
Bilangan bulat atau integer atau disingkat int dinyatakan tanpa titik.
1
>>> a = 10 a ditambah 2
>>> a + 2 12
1 2
a dikurang 4
1 2
>>> a
−
4
6 a dikali 5
1 2
>>> a
*
5
50 a dibagi dengan 3
1 2
>>> a
/
3
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
**
2
BAB 4.
TIPE DATA
4.2.1
Konversi Tipe Data
18
Konversi dari string menjadi bilangan bulat menggunakan fungsi int().
1 2
>>>
int ( '10 ')
10 int() juga dapat digunakan untuk pembulatan.
1 2
>>>
int (10.2)
10 Untuk pembulatan yang mendekati gunakan round().
1 2 3 4
>>> r o u n d ( 1 0 . 4 ) 10 >>> r o u n d ( 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
>>>
float ( '10.5 ')
10.5
4.2.2
Formatting
Menuliskan bilangan ke dalam string bisa menggunakan formatting.
1
>>> a = 3
2
>>> b = 2
3
>>> p r i n t
4
3 + 2 = 5
'% s + %s = %s ' % ( a ,
b,
a+b )
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.
1
>>> p r i n t
2
3 + 2 = 5
'%d + %d = %d ' % ( a ,
b,
a+b )
Untuk bilangan pecahan (oat) gunakan %f.
1
>>> a = 3 . 5
2
>>> p r i n t
3
'% 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
19
BAB 4.
TIPE DATA
4.2.3
Kalkulator
Membuat kalkulator tidaklah sulit, Anda cukup gunakan eval().
Buatlah le
calc.py berikut ini.
1 2 3 4 5
while if not break print
Listing 4.1: calc.py
True :
h i t u n g = raw_input ( ' Hitung
:
')
hitung :
eval ( hitung )
Jalankan.
1
$
2
Hitung
3
5
python
4
Hitung
5
49
6
Hitung
7
13
8
Hitung
9
$
c a l c . py
:
2+3
:
7 ** 2
:
7 + 2
*
3
:
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
print
in
' Bummi ' :
ch ,
hasilnya:
1
Bummi Perhatikan penggunaan koma pada print yang berarti jangan ganti baris. Mari kembali ke modus interaktif untuk mencoba list.
BAB 4.
20
TIPE DATA
Gambar 4.1: Struktur list
1
>>> d a t a =
2
>>> p r i n t
3 4 5
[ ' Bummi Dwi
P u t e r a ' , ' Bogor ' , ' Menggambar ' , 2 4 ]
data [ 0 ]
' Bummi Dwi >>> p r i n t
Putera ' data [ 1 ]
' Bogor ' Tampilkan elemen terakhir:
1 2
data [ −1]
>>> p r i n t 24
Elemen kedua dari terakhir:
1 2
data [ −2]
>>> p r i n t
' 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
>>> p r i n t
data [ 0 : 2 ]
[ ' Bummi Dwi
Putera ' ,
' Bogor ' ]
Atau cukup ditulis tanpa 0:
1 2
>>> p r i n t
data [ : 2 ]
[ ' Bummi Dwi
Putera ' ,
' Bogor ' ]
Tampilkan 3 elemen terakhir:
1
>>> p r i n t
2
[ ' Bogor ' ,
data [
−3:]
' Menggambar ' ,
24]
Ada baiknya kita pahami cara kerjanya. Pemenggalan list bekerja dengan batas elemen. sebagai list.
Gambar 4.1 adalah contoh string Python yang bisa dianggap
Cermati baik-baik bagaimana bekerja dengan elemen dan batas
elemen (untuk pemenggalan).
21
BAB 4.
TIPE DATA
4.3.2
Keberadaan Elemen
Anda memiliki daftar nama buah yang dapat dicari oleh user.
Setelah 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
daftar =
2
while if not print break if in print else print
3 4 5 6 7 8 9 10 11
[ ' j e r u k ' , ' mangga ' , ' a p e l ' , ' p i s a n g ' , ' jambu ' ]
True :
cari
= raw_input ( ' C a r i
buah :
')
cari :
cari
' Selesai ' daftar :
( ' Ditemukan ' )
:
( ' Tidak
ditemukan ' )
Jalankanlah.
1
$
2
Cari
3
Ditemukan
python
p r o d u k . py
buah :
4
Cari
5
Tidak
6
Cari
7
Selesai
buah :
mangga duku
ditemukan buah :
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
[ ' c a t . py ' ]
python
c a t . py
Jalankan lagi dengan input parameter.
1
$
2
[ ' c a t . py ' ,
python
c a t . py
/ etc / hosts
'/ 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.
1 2
22
TIPE DATA
Listing 4.4: cat1.py
import
sys
3
f i l e n a m e = sys . argv [ 1 ]
4
print
5 6
f = open ( f i l e n a m e ) f . read ( )
f . close () Jalankan.
1
$
2
127.0.0.1
localhost
3
127.0.1.1
compaq
python
c a t 1 . py
/ etc / hosts compaq
4 5
# The
following
lines
are
desirable
for
IPv6
capable
hosts i p 6 −l o c a l h o s t
6
::1
localhost
7
fe00 : : 0
i p 6 −l o c a l n e t
8
ff00 ::0
i p 6 −m c a s t p r e f i x
9
ff02 ::1
i p 6 −a l l n o d e s
10
ff02 ::2
ip6−a l l r o u t e r s
11
ff02 ::3
ip6−a l l h o s t s
i p 6 −l o o p b a c k
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
Traceback
3 4 5
python File
c a t 1 . py ( most
" c a t . py " ,
recent line
call 3,
last ) :
i n <module>
f i l e n a m e = sys . argv [ 1 ] IndexError :
list
index
out
of
range
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
import if not print
Listing 4.5: cat2.py
sys
sys . argv [ 1 : ] : ( ' Cara
menggunakannya :
sys . argv [ 0 ] )
5
sys . exit ()
6 7
f i l e n a m e = sys . argv [ 1 ]
8
print
9 10
f = open ( f i l e n a m e ) f . read ( )
f . close ()
p y t h o n %s
' %
BAB 4.
23
TIPE DATA
Jalankanlah tanpa input parameter,
1
$
2
Cara
python
c a t 2 . py
menggunakannya :
python
c a t . py
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
['cat.py', '/etc/hosts'] Dengan begitu
if not sys.argv[1:]: bisa berarti
if not ['/etc/hosts']:
Nilai
BAB 4.
24
TIPE DATA
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
>>> d a f t a r =
2
>>> d a f t a r
3
[]
[] Tambahkan mangga.
1
>>> d a f t a r . append ( ' mangga ' )
2
>>> d a f t a r
3
[ ' mangga ' ] Bisa juga dengan cara lain.
1
>>> d a f t a r += [ ' p i s a n g ' ]
2
>>> d a f t a r
3
[ ' mangga ' ,
' pisang ' ]
Kita akan ubah elemen kedua (index ke 1) dari pisang menjadi jeruk.
1
>>> d a f t a r [ 1 ]
2
>>> d a f t a r
3
[ ' mangga ' ,
=
' jeruk '
' jeruk ' ]
Selanjutnya mangga dihapus yang berarti elemen pertama atau index ke 0.
1
>>> d e l
2
>>> d a f t a r
3
[ ' jeruk ' ]
daftar [ 0 ]
BAB 4.
4.4
25
TIPE DATA
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
daftar = {
2
2:
' jeruk ' ,
3
7:
' mangga ' ,
4
4:
' pisang ' ,
5
3:
' jambu ' ,
6
9:
' apel ' ,
7
}
8
print
9
daftar
Jalankan.
1
$
2
{9:
python
p r o d u k 1 . py
' apel ' ,
2:
' jeruk ' ,
3:
' jambu ' ,
4:
' pisang ' ,
7:
'
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
daftar = {
2
2:
' jeruk ' ,
3
7:
' mangga ' ,
4
4:
' pisang ' ,
5
3:
' jambu ' ,
6
9:
' apel ' ,
7
}
8 9 10 11 12 13 14
while if not print break if in True :
k e y = r a w _ i n p u t ( ' Kode key :
key
' Selesai ' daftar :
barang :
')
BAB 4.
15
else
16 17
26
TIPE DATA
print print
d a f t a r [ key ]
:
' Kode
barang ' ,
key ,
' tidak
ditemukan '
Cobalah.
1
$
2
Kode
barang :
3
Kode
barang
4
Kode
barang :
5
Kode
barang
6
Kode
barang :
7
Selesai
python
p r o d u k 2 . py 5 5
tidak
ditemukan
tidak
ditemukan
9 9
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
Kode
barang :
3
Kode
barang
python
4
tidak
5
Kode
6
Selesai
p r o d u k 2 . py 9 [ '9 ']
ditemukan barang :
Dengan memberikan kurung siku pada variabel maka akan tampak bahwa key adalah string.
Terlihat adanya kutip tunggal pada '9'.
mengubah key agar bertipe integer ? Gunakan fungsi int().
Lalu bagaimana
BAB 4.
27
TIPE DATA
Listing 4.8: produk3.py
1
daftar = {
2
2:
' jeruk ' ,
3
7:
' mangga ' ,
4
4:
' pisang ' ,
5
3:
' jambu ' ,
6
9:
' apel ' ,
7
}
8 9 10 11 12 13 14 15 16 17 18
while if not print break if in print else print True :
k e y = r a w _ i n p u t ( ' Kode
barang :
')
key : ' Selesai '
key = i n t ( key ) key
daftar : d a f t a r [ key ]
:
' Kode
barang ' ,
key ,
' tidak
ditemukan '
Jalankan lagi.
1
$
2
Kode
barang :
3
Kode
barang
4
Kode
barang :
python
5
apel
6
Kode
7
Selesai
p r o d u k 3 . py 5 [5]
tidak
ditemukan
9
barang :
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
>>> i m p o r t
2
>>> t = t i m e . l o c a l t i m e ( )
3
>>> t
4
time
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 ,
tm_sec =47 , tm_wday=4 , tm_yday
t m _ i s d s t =0)
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.
28
TIPE DATA
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
>>> t i m e . t i m e ( ) 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
>>> t i m e . 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 ,
tm_sec =0 , tm_wday=3 , tm_yday=1 ,
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():
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
>>> a w a l = t i m e . mktime ( ( 2 0 1 0 , 8 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ) )
2
>>> a k h i r = t i m e . mktime ( ( 2 0 1 0 , 8 , 1 7 , 0 , 0 , 0 , 0 , 0 , 0 ) )
3
>>> a k h i r
4
1382400.0
−
awal
Itu adalah jumlah detiknya. Untuk mendapatkan hari kita perlu mengolahnya kembali.
1 2
>>> ( a k h i r
−
awal )
/
24
16.0 Sehingga diperoleh 16 hari.
/
60
/
60
BAB 4.
29
TIPE DATA
Modul datetime Menghitung hari lebih mudah menggunakan modul datetime.
1
>>> f r o m
2
>>> a w a l = d a t e ( 2 0 1 0 , 8 , 1 )
3
>>> a k h i r = d a t e ( 2 0 1 0 , 8 , 1 7 )
4
>>> d = a k h i r
5
>>> d . d a y s
6
datetime
−
import
date
awal
16 Dengan modul ini kita juga dapat menghitung jumlah detik sejak 16 Agustus 2010 jam 22:00 hingga 17 Agustus 2010 jam 10:00.
1
>>> f r o m
2
>>> a w a l = d a t e t i m e ( 2 0 1 0 , 8 , 1 6 , 2 2 , 0 , 0 )
3
>>> a k h i r = d a t e t i m e ( 2 0 1 0 , 8 , 1 7 , 1 0 , 0 , 0 )
4
>>> d = a k h i r
5
>>> d . s e c o n d s
6
datetime
−
import
datetime
awal
43200 Untuk mendapatkan jumlah jamnya:
1 2
>>> d . s e c o n d s 12
/
60
/
60
Bab 5 Modularitas
Mari kita buat program yang menghitung nilai faktorial.
Apa itu faktorial ?
Berikut ini contohnya:
5! 4! 3! 2! 1! 0! -1!
= = = = = = =
5 4 3 2 1 1 1
* * * *
4 3 2 1
* * * =
3 * 2 * 1 = 120 2 * 1 = 24 1 = 6 2
Dengan demikian rumus faktorial memiliki ketentuan: 1. Jika n < 2 maka n! = 1 2. n! = n * (n-1)! Selanjutnya kita akan buat program yang akan menanyakan nilai n dan menampilkan nilai faktorial-nya.
1 2 3 4 5 6 7 8 9 10 11 12 13
print while if not break if else for in * print
Listing 5.1: faktorial1.py
' Menghitung
nilai
faktorial '
True :
n = raw_input ( ' n =
')
n:
n = int (n) n <
2:
f = 1 :
f = 1 i
f = f
'%d !
r a n g e ( 1 , n+1) : i
= %d ' % ( n ,
f) 30
BAB 5.
31
MODULARITAS
Jalankan.
1
$
2
Menghitung
3
n = 2
python
4
2! = 2
5
n = 3
6
3! = 6
7
n = 4
8
4 ! = 24
9
n =
10
f a k t o r i a l 1 . py nilai
faktorial
$ 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.
Baris 6 - 12
Anda juga harus menye-
suaikan variabel n yang menjadi masukannya, karena n tidak lagi berasal dari 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
dengan source yang mengurus tampilan.
Faktorial adalah contoh sederhana,
bagaimana kalau nanti Anda diminta membuat rumus lainnya yang jauh lebih rumit ?
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:
return for in * return
1
f = 1 i
f = f
r a n g e ( 1 , n+1) : i
f
print while if not
' Menghitung
nilai
True :
n = raw_input ( ' n = n:
')
faktorial '
BAB 5.
32
MODULARITAS
break
14 15
n = int (n)
16
print
hasil
17
=
f a k t o r i a l (n)
'%d !
= %d ' % ( n ,
hasil )
Jalankan.
1
$
2
Menghitung
3
n = 5
python
4
5 ! = 120
5
n = 2
6
2! = 2
7
n = 1
8
1! = 1
9
n =
−1
10
− 1!
= 1
11
n =
f a k t o r i a l 2 . py nilai
faktorial
Hasilnya memang sama saja, namun kini source faktorial lebih mudah dibaca dan di-copy-paste.
Perhatikan juga penggunaan return yang membuat alur
keluar dari fungsi. Apa ini sudah cukup modular ? Jawabannya belum.
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
from
2
n = 5
3
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
Listing 5.3: faktorial.py
faktorial (a) : a <
2:
i
a = a
1 range (1 , a ) : i
a
Lalu buatlah cobafaktorial.py yang menggunakan modul faktorial ini.
1
from
Listing 5.4: cobafaktorial.py
faktorial
import
faktorial
BAB 5.
2 3 4 5 6 7 8
33
MODULARITAS
print while if not break
' Menghitung
nilai
faktorial '
True :
n = raw_input ( ' n =
')
n:
n = int (n)
9
hasil
print
10
=
f a k t o r i a l (n)
'%d !
= %d ' % ( n ,
hasil )
Jalankan.
1
$
2
Menghitung
3
n = 5
python
4
5 ! = 120
5
n = 2
6
2! = 2
7
n =
c o b a f a k t o r i a l . py Nilai
Faktorial
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
Listing 5.5: faktorial.py
faktorial (a) : a <
2:
i
a = a
1 range (1 , a ) : i
a
__name__ ==
'__main__ ' :
' Menghitung
nilai
True :
n = raw_input ( ' n = n:
n = int (n)
')
faktorial '
BAB 5.
15
hasil
=
print
16
34
MODULARITAS
f a k t o r i a l (n)
'%d !
= %d ' % ( n ,
hasil )
Perhatikan bahwa __name__ adalah name yang diawali dan diakhiri oleh dua underscore. Begitu juga dengan __main__. Kemudian jalankan.
1
$
2
Menghitung
3
n = 3
python
4
3! = 6
5
n = 4
6
4 ! = 24
7
n =
f a k t o r i a l . py nilai
faktorial
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.
1
$
python
5.3
c o b a f a k t o r i a l . py
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
>>> i m p o r t
2
>>> s y s . p a t h
sys
3
[ ' ' ,
4
' / u s r / l i b / python2 . 6 ' ,
5
' / u s r / l i b / p y t h o n 2 . 6 / p l a t −l i n u x 2 ' ,
6
' / u s r / l i b / p y t h o n 2 . 6 / l i b −t k ' ,
7
' / u s r / l i b / p y t h o n 2 . 6 / l i b −o l d ' ,
8
' / u s r / l i b / p y t h o n 2 . 6 / l i b −d y n l o a d ' ,
9
' / 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 ' ,
10
' / 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 ' ,
11
' / 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
12
' / u s r / l i b / pymodules / python2 . 6 ' ,
13
' / 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
14
' / u s r / l i b / pymodules / python2 . 6 / gtk
15
' / 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 ' ]
−0.10 ' ,
−2.0 ' , −2.0 ' ,
BAB 5.
35
MODULARITAS
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
1
$
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.
1
$
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
>>> i m p o r t
2
>>> s y s . p a t h . append ( ' / home/ s u g i a n a / l i b ' )
3
>>> f r o m
sys
faktorial
import
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
def if
3 4
faktorial (a) : a <
2:
return return
a
1
*
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
>>> i m p o r t
2
>>>
3 4 5
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 ' )
' id_ID . UTF8 ' >>>
l o c a l e . format ( '%.2 f ' ,
10000 ,
True )
'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.
1
37
FUNGSI
Traceback
( most
recent
call
last ) :
2
File
"< s t d i n >" ,
3
File
" / u s r / l i b / p y t h o n 2 . 6 / l o c a l e . py " ,
line
1,
i n <module> line
513 ,
in
setlocale
4 5
return
_setlocale ( category ,
l o c a l e . Error :
unsupported
locale
locale ) setting
Berarti Anda perlu memasang format id_ID.UTF-8 pada sistem. Keluarlah dari modus interaktif dan jalankan:
1
$
2
Generating
locale
−g e n
Generation
id_ID . UTF−8
locales . . .
id_ID . UTF− 8 . . .
3 4
sudo
up−t o −d a t e
complete .
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
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 ' )
3 4 5 6 7 8 9 10
def return
uang ( n i l a i ,
if
11 12
__name__ ==
print print print
p e c a h a n =2) :
l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n ,
'__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
10.000 ,00
python
3
10.000 ,30
4
10.000
uang . py
Perhatikan bagian
'%%.%df' % pecahan
nilai ,
True )
BAB 6.
38
FUNGSI
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
Listing 6.2: uang.py
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 ' )
3 4 5 6 7
def if
9
12
16 17
p e c a h a n=None ) : None :
t y p e ( n i l a i ) == t y p e ( 0 ) : :
pecahan = 2
11
15
is
pecahan = 0
10
14
pecahan
if else return
8
13
uang ( n i l a i ,
if
l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n ,
__name__ ==
print print print
nilai ,
True )
'__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.
39
FUNGSI
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
Listing 6.3: uang.py
import from
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
def if
import
ribu ( nilai ,
is
pecahan
if else return def if return
IntType
p e c a h a n=None ) : None :
t y p e ( n i l a i ) == I n t T y p e :
pecahan = 0 :
pecahan = 2 l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n ,
uang ( n i l a i , tanda
is
t a n d a=None ) :
l o c a l e . l o c a l e c o n v ( ) [ ' currency_symbol ' ]
'%s%s ' % ( t a n d a ,
__name__ ==
print print print print print print
True )
None :
tanda =
if
p e c a h a n=None ,
nilai ,
ribu ( nilai ,
pecahan ) )
'__main__ ' :
ribu (10000) ribu (10000.3) ribu (10000.5 ,
4)
uang ( 1 0 0 0 0 . 7 ) uang ( 1 0 0 0 0 . 7 ,
2,
uang ( 1 0 0 0 0 . 7 ,
t a n d a= ' $ ' )
'$ ' )
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.
40
FUNGSI
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
>>> f r o m
2
>>> uang ( 1 0 0 0 0 )
3
'10.000 '
uang
import
uang
Bab 7 Database
Database atau penyimpan data biasa digunakan untuk aplikasi bisnis seperti kasir (point of sales), accounting, payroll, dsb. Pasanglah
1
$
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:
1
$
sudo
su
Masukkan password user yang Anda gunakan ketika login pertama kali. Kini Anda telah menjadi root, dan mulailah sebagai user postgres:
1
# su
−
postgres
Kini Anda sudah menjadi user postgres.
Menurut ketentuan default, bila
user Linux dan user PostgreSQL sama maka tidak perlu password untuk login ke PostgreSQL server. Sekarang mulailah membuat user database:
−P
1
$
2
Enter
password
3
Enter
it
4
Shall
the
new
role
be
a
5
Shall
the
new
role
be
allowed
to
create
databases ?
the
new
role
be
allowed
to
create
more new
createuser
ilham for
new
role :
again : superuser ?
( y/n )
n ( y/n )
n
6
Shall
y/n )
roles ?
(
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:
1
$
dropuser
ilham 41
BAB 7.
42
DATABASE
Selanjutnya membuat database:
1
$
−O
createdb
ilham
totalindo
Ini artinya kita membuat database bernama totalindo yang dimiliki oleh user ilham. Untuk menghapusnya:
1
$
dropdb
totalindo
Untuk melihat daftar database bisa menggunakan psql:
1
$
2
psql
(8.4.4)
3
Type
" help "
psql for
help .
4 5
p o s t g r e s=# \ l
6
List
7 8 9 10 11 12 13
of
databases
Name
|
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 )
14 15
p o s t g r e s=# Untuk keluar gunakan \q atau tekan Ctrl-D:
1
p o s t g r e s=# \ q
2
$
3
postgres
whoami
Anda masih sebagai user postgres, keluarlah dengan perintah logout atau tekan Ctrl-D:
1
$
2
# whoami
3
logout
root Kini Anda sebagai root, keluarlah lagi dengan perintah logout agar kembali sebagai user biasa:
1
# logout
2
$
3
sugiana
whoami
Sekarang kita login ke database totalindo sebagai user ilham:
−U
1
$
2
Password
psql
3
psql
4
SSL
ilham for
totalindo
user
−h
localhost
ilham :
(8.4.4) connection
( cipher :
DHE−RSA−AES256−SHA,
bits :
256)
BAB 7.
5
Type
43
DATABASE
" help "
for
help .
6 7
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
t o t a l i n d o=> CREATE TABLE p e g a w a i (
2
t o t a l i n d o (>
3
t o t a l i n d o (> nama
4
t o t a l i n d o (>
5
t o t a l i n d o (> PRIMARY KEY( i d )
6 7
t o t a l i n d o (>
id
serial
t g l _ l a h i r DATE NOT NULL, ) ;
NOTICE : CREATE TABLE pegawai_id_seq "
8
will
for
create
serial
implicit
column
" pe g aw a i_ p ke y "
t o t a l i n d o=> Perhatikan prompt
totalindo=> dan bedakan dengan prompt
totalindo(>
for
table
sequence
"
" pegawai . i d "
NOTICE : CREATE TABLE / PRIMARY KEY w i l l index
9
NOT NULL,
v a r c h a r ( 3 0 ) NOT NULL,
create
implicit
" p e g a w a i " CREATE TABLE
BAB 7.
44
DATABASE
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
t o t a l i n d o=> \ d t
2 3 4 5 6
List Schema
|
of
relations
Name
|
Type
|
Owner
−−−−−−−−+−−−−−−−−−+−−−−−−−+−−−−−−− public (1
|
pegawai
|
table
|
ilham
row )
7 8
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
t o t a l i n d o=> \ d
2 3
pegawai
Table Column
" p u b l i c . pegawai "
|
Type
| Modifiers
4 5
−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−− id
|
integer
|
not
null
n e x t v a l ( ' pegawai_id_seq ' : : r e g c l a s s )
6
nama
|
character
7
tgl_lahir
|
date
8 9
|
not
null
|
not
null
Indexes : " pe g aw ai _ pk e y " PRIMARY KEY,
10 11
varying (30)
t o t a l i n d o=>
btree
( id )
default
BAB 7.
45
DATABASE
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).
Jadi pendenisian suatu eld menjadi serial akan
membuat: 1. Tipenya menjadi integer (bilangan bulat). 2. Memiliki default value nomor urut berikutnya.
Nomor urut berikutnya
tersimpan dalam sebuah sequence. Apa itu sequence ?
7.1.1
Sequence
Sequence mirip tabel, tepatnya
tabel satu record
yang berisi data untuk ke-
butuhan nomor urut. Pada contoh di atas sequence bernama pegawai_id_seq otomatis terbentuk saat eld id pada tabel pegawai didenisikan sebagai serial. Data pada sequence bisa dilihat sebagaimana tabel menggunakan perintah SELECT.
1 2 3 4
t o t a l i n d o=> SELECT sequence_name
|
*
FROM p e g a w a i _ i d _ s e q
last_value
|
is_called
; |
−−−−−−−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−−−−−+ pegawai_id_seq
|
1
|
f
|
5 6
(1
row ) Sekarang kita lihat isi tabel pegawai.
1 2 3 4
t o t a l i n d o=> SELECT id
|
nama
|
*
FROM p e g a w a i ;
tgl_lahir
−−−−+−−−−−−+−−−−−−−−−−− (0
rows ) Tampak tabel pegawai masih kosong, mari tambah datanya.
1
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 )
2
t o t a l i n d o −> VALUES ( ' Bummi Dwi
3
INSERT 0
Putera ' , '1985 −8 −17 ') ;
1
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.
46
DATABASE
1
t o t a l i n d o=> INSERT INT
2
t o t a l i n d o −>
3
ERROR:
syntax
4
LINE
INSERT INT
1:
5 6
p e g a w a i ( nama , t g l _ l a h i r )
; error
at
or
near
"INT"
p e g a w a i ( nama , t g l _ l a h i r )
^ t o t a l i n d o=> 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
FROM p e g a w a i ; |
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 SE-
LECT 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
t o t a l i n d o=> SELECT sequence_name
|
*
FROM p e g a w a i _ i d _ s e q
last_value
|
;
is_called
−−−−−−−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−−−−− pegawai_id_seq
|
1
|
t
5 6
(1
row ) Bandingkan dengan nilai-nilai pegawai_id_seq pada SELECT sebelum IN-
SERT. 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
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 )
2
t o t a l i n d o −> VALUES ( ' A r i e f
3
INSERT 0
4
t o t a l i n d o=> SELECT
5 6 7 8 9
id
S e t i a d i ' , '1972 −5 −2 ') ;
1
|
*
nama
FROM p e g a w a i ; |
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 pega-
wai_id_seq.
1
t o t a l i n d o=> SELECT
*
FROM p e g a w a i _ i d _ s e q
;
BAB 7.
2 3
47
DATABASE
sequence_name
|
last_value
|
is_called
|
−−−−−−−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−−−−−+
4
pegawai_id_seq
|
2
|
t
|
1
5 6
(1
row ) 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.
1
$
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.
1
$
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
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 )
2
t o t a l i n d o −> VALUES ( ' Cecep
3
INSERT 0
4
t o t a l i n d o=> SELECT
5 6 7 8 9 10
id
|
Zahrudin ' , '1 −6 −1972 ') ;
1 nama
*
FROM p e g a w a i ; |
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.
1 2 3 4 5 6 7
t o t a l i n d o=> SELECT id
48
DATABASE
|
*
FROM p e g a w a i ORDER BY nama ;
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
|
*
FROM p e g a w a i WHERE i d = 1 ;
nama
|
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 ter-
tua.
*
1
t o t a l i n d o=> SELECT
2
t o t a l i n d o −> WHERE d a t e _ p a r t ( ' y e a r ' ,
3
t o t a l i n d o −> ORDER BY t g l _ l a h i r ;
4 5 6 7 8
id
|
nama
FROM p e g a w a i
|
t g l _ l a h i r ) = 1972
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
t o t a l i n d o=> SELECT
*
FROM p e g a w a i
2
WHERE d a t e _ p a r t ( ' y e a r ' ,
3
ORDER BY t g l _ l a h i r
4 5 6 7 8
id
|
nama
t g l _ l a h i r ) = 1972
DESC ; |
tgl_lahir
−−−−+−−−−−−−−−−−−−−−−+−−−−−−−−−−−− 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
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 )
2
t o t a l i n d o −> VALUES ( ' N i t a
3
INSERT 0
1
Pandria ' , '19 −9 −1976 ') ;
BAB 7.
49
DATABASE
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
t o t a l i n d o=> ALTER TABLE p e g a w a i ADD p r i a
b o o l e a n NOT NULL
DEFAULT t r u e ;
2
ALTER TABLE 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
FROM 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
p r i a = F a l s e WHERE i d = 4 ;
UPDATE 1 t o t a l i n d o=> SELECT id
|
*
nama
FROM 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 SE-
LECT, 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 FROM p e g a w a i WHERE i d = 2 ; DELETE 1 t o t a l i n d o=> SELECT id
|
nama
*
FROM p e g a w a i ORDER BY nama ; |
tgl_lahir
|
pria
−−−−+−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−
BAB 7.
6
1
|
Bummi Dwi
7
3
|
Cecep
8
4
|
Nita
9
(3
50
DATABASE
Putera
Zahrudin Pandria
|
1985 − 08 − 17
|
t
|
1972 − 06 − 01
|
t
|
1976 − 09 − 19
|
f
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 ?
Silahkan
hapus dengan perintah DROP TABLE.
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
t o t a l i n d o=> SELECT i d ,
nama ,
now ( )
−
t g l _ l a h i r FROM
p e g a w a i ORDER BY t g l _ l a h i r ;
2 3
id
|
nama
|
? column ?
−−−−+−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−
4
2
|
Arief
Setiadi
|
13957
days
11:21:54.083022
5
3
|
Cecep
Zahrudin
|
13927
days
11:21:54.083022
6
4
|
Nita
7
1
|
Bummi Dwi
8
(4
Pandria Putera
|
12356
|
9102
days days
11:21:54.083022 11:21:54.083022
rows ) Kolom ketiga berisi usia, tapi PostgreSQL tidak tahu harus dinamakan apa,
sehingga jadilah bernama ?column?. Ada baiknya kita berikan nama usia.
−
1
t o t a l i n d o=> SELECT i d ,
2
t o t a l i n d o −> FROM p e g a w a i ORDER BY t g l _ l a h i r ;
3 4
id
|
nama
nama ,
now ( )
|
tgl_lahir
AS
usia
usia
−−−−+−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−
5
2
|
Arief
Setiadi
|
13957
days
11:28:34.001142
6
3
|
Cecep
Zahrudin
|
13927
days
11:28:34.001142
BAB 7.
7
4
|
Nita
8
1
|
Bummi Dwi
9
(4
51
DATABASE
Pandria Putera
|
12356
|
9102
days days
11:28:34.001142 11:28:34.001142
rows ) Satuan usia menggunakan hari tentu saja kurang nyaman dibaca. Sebaiknya
kita ganti rumusnya:
usia = tahun sekarang - tahun kelahiran sehingga query-nya menjadi:
1
t o t a l i n d o=> SELECT i d ,
2
t o t a l i n d o −> e x t r a c t ( y e a r tgl_lahir )
3 4 5
AS
nama , from
−
ext ract ( year
from
t o t a l i n d o −> FROM p e g a w a i ORDER BY t g l _ l a h i r ; id
|
nama
|
usia
−−−−+−−−−−−−−−−−−−−−−−−+−−−−−−
6
2
|
Arief
Setiadi
|
38
7
3
|
Cecep
Zahrudin
|
38
8
4
|
Nita
|
34
9
1
|
Bummi Dwi
|
25
10
now ( ) )
usia
(4
Pandria Putera
rows ) Perintah SQL (query) di atas dapat disimpan dalam sebuah VIEW.
1
t o t a l i n d o=> CREATE VIEW v_pegawai AS SELECT i d ,
2
ext ract ( year AS
from
now ( ) )
−
ext ract ( year
from
nama ,
tgl_lahir )
usia
3
FROM p e g a w a i ORDER BY t g l _ l a h i r ;
4
CREATE VIEW Selanjutnya kita tampilkan VIEW tadi layaknya sebuah tabel.
1 2 3
t o t a l i n d o=> SELECT id
|
*
FROM v_pegawai ;
nama
|
usia
−−−−+−−−−−−−−−−−−−−−−−−+−−−−−−
4
2
|
Arief
Setiadi
|
38
5
3
|
Cecep
Zahrudin
|
38
6
4
|
Nita
|
34
7
1
|
Bummi Dwi
|
25
8
(4
Pandria Putera
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
*
FROM v_pegawai ORDER BY nama ; |
usia
BAB 7.
3
−−−−+−−−−−−−−−−−−−−−−−−+−−−−−−
4
2
|
Arief
5
1
|
Bummi Dwi
6
3
|
Cecep
|
Nita
7 8
52
DATABASE
4 (4
Setiadi
|
Putera
Zahrudin Pandria
38
|
25
|
38
|
34
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. Listing 7.1: pegawai.sql
1 2 3 4 5 6 7 8 9
CREATE TABLE varchar NOT NULL date NOT NULL NOT NULL DEFAULT true PRIMARY KEY pegawai (
id
serial ,
nama pria
,
,
boolean
) ;
INSERT INTO INSERT INTO INSERT INTO INSERT INTO
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 ' ,
true
true true false ) ;
p e g a w a i ( nama , t g l _ l a h i r , p r i a )
Z a h r u d i n ' , ' 1972 − 6 − 1 ' ,
12
) ;
p e g a w a i ( nama , t g l _ l a h i r , p r i a )
S e t i a d i ' , ' 1972 − 5 − 2 ' ,
11
,
( id )
Dwi
10
(30)
tgl_lahir
) ;
p e g a w a i ( nama , t g l _ l a h i r , p r i a )
P a n d r i a ' , ' 1976 − 9 − 19 ' ,
) ;
VALUES VALUES VALUES VALUES
( ' Bummi ( ' Arief ( ' Cecep ( ' Nita
Lalu di psql jalankan le tersebut:
1
t o t a l i n d o=> \ i
2
p s q l : s q l / pegawai . s q l : 7 : implicit
pegawai . s q l
sequence
NOTICE :
CREATE TABLE
" pegawai_id_seq "
for
will
serial
create column
" pegawai . i d "
3
p s q l : s q l / pegawai . s q l : 7 : KEY
will
table
create
NOTICE :
implicit
" pegawai "
4
CREATE TABLE
5
INSERT 0
1
6
INSERT 0
1
7
INSERT 0
1
8
INSERT 0
1
Kalau Anda menemui kegagalan
CREATE TABLE / PRIMARY
index
" p e ga w ai _ pk e y "
for
BAB 7.
53
DATABASE
No such file or directory cobalah menuliskan nama le secara fullpath, misalnya.
1
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.
1
$ pg_dump
−U
ilham
−h
totalindo
localhost
−F
t
−f
totalindo . sql . tar Mulailah untuk latihan restore, yaitu dengan membuat database lain bernama totalindo_1 (lihat pembuatan database di awal bab ini). Lalu jalankan:
1
$
−U
pg_restore
ilham
−h
localhost
−d
totalindo_1
totalindo . sql . tar Anda juga bisa backup menggunakan format teks biasa (plaintext):
1
$ pg_dump
−U
ilham
−h
totalindo
localhost
−f
totalindo .
sql dan restore dengan psql:
1
$
psql
−U
ilham
totalindo_1
−h
localhost
−f
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.
−U
1
$
2
psql
(8.4.4)
3
Type
" help "
psql
ilham for
template1
−h
localhost
help .
4 5
p o s t g r e s=# \ l
6
List
7 8 9 10 11 12 13 14
of
databases
Name
|
Owner
|
Encoding
|
Collation
|
−−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−−+ 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.
54
DATABASE
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
# su
3
$
sudo
su
−
postgres
createdb
−O
ilham
−T
template0
−E
sql_ascii
totalindo_2
Kembali ke psql dan lihat daftar database. Karena masih sebagai user postgres maka cukup ketik psql.
1
$
2
psql
(8.4.4)
3
Type
" help "
psql for
help .
4 5
p o s t g r e s=# \ l
6
List
7 8 9 10 11 12 13 14 15
of
databases
Name
|
Owner
|
Encoding
|
Collation
|
−−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−+−−−−−−−−−−−−−+ 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
t o t a l i n d o=> SELECT now ( ) ;
2
now
3
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 2010 − 08 − 24 1 6 : 0 6 : 5 8 . 6 7 8 0 5 6 + 0 7
4 5
(1
row ) Keunikan fungsi pada PostgreSQL adalah pada nama dan susunan masu-
kannya (input parameter). Perhatikan contoh rata kanan berikut ini.
BAB 7.
1
t o t a l i n d o=> SELECT l p a d ( ' A' , 5 ) ;
2 3
lpad
−−−−−−−
4 5
55
DATABASE
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
t o t a l i n d o=> SELECT
2
length
3
−−−−−−−−
4 5
l e n g t h ( l p a d ( ' A' , 5 ) ) ;
5 (1
row ) Sekarang kita membutuhkan awalan '0' pada sebuah nomor, misalnya '00001'.
Ini lazim kita temui pada beberapa laporan. Fungsi apa yang akan digunakan. Jawabannya masih menggunakan fungsi lpad().
1
t o t a l i n d o=> SELECT l p a d ( ' 1 ' , 5 , ' 0 ' ) ;
2 3
lpad
−−−−−−−
4 5
00001 (1
row ) Di sinilah letak keunikan sebuah fungsi pada PostgreSQL. Fungsi lpad()
bisa menerima masukan dua buah atau tiga buah.
Jika dua buah saja yang
digunakan maka lpad() akan mengawalinya dengan spasi. Namun jika tiga buah masukan, maka lpad() akan mengawalinya sesuai dengan karakter pada masukan ketiga. Cermatilah baik-baik. Jadi bila kita ingin membuat beberapa fungsi dengan nama sama maka harus dibedakan dengan: 1. Jumlah masukannya. 2. Jika jumlah masukannya sama maka bedakan tipe data setiap masukan tersebut.
7.4.1
PL/pgSQL
Salah satu yang membuat PostgreSQL begitu mudah dikembangkan adalah fungsi tersebut bisa ditulis dalam berbagai bahasa.
Dimana Anda bisa me-
nulis fungsi dalam bahasa pgSQL, Python, Perl, hingga Tcl. bahasa yang didukungnya adalah plpgsql.
Secara default
Namun Anda perlu memasangnya
terlebih dahulu pada database yang dimaksud.
1
$
sudo
su
postgres
−c
" createlang
plpgsql
totalindo "
BAB 7.
56
DATABASE
Selanjutnya siapkanlah dua konsole, yang pertama untuk
1
$
psql
−U
ilham
totalindo
−h
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
Listing 7.2: hello.sql REPLACE FUNCTION
hello ()
RETURNS t e x t
LANGUAGE p l p g s q l
AS
$$
BEGIN RETURN
END
' Hello
world . ' ;
$$ ;
Setelah disimpan jalankan di psql tadi:
1 2
t o t a l i n d o=> \ i
hello . sql
CREATE FUNCTION lalu cobalah
1
t o t a l i n d o=> SELECT
2 3 4 5
hello () ;
hello
−−−−−−−−−−−−−− Hello (1
world .
row )
Transaksi Perbankan Kita sudah punya tabel pegawai dimana setiap pegawai dianggap sebagai nasabah pada perusahaan ini.
Saat mendapat gaji saldonya bertambah.
ambil gaji saldonya berkurang.
Saat
Saat kas bon (pinjam) saldonya juga berku-
rang. Jadi saldo juga bisa negatif yang berarti pegawai tersebut punya hutang ke perusahaan. Pertama kita butuh eld saldo pada tabel pegawai.
1
ALTER TABLE
pegawai
ADD
saldo
float NOT NULL DEFAULT
0;
Kemudian untuk mencatat aktivitas transaksi kita memerlukan tabel transaksi berikut ini. Listing 7.3: transaksi.sql
1 2 3
CREATE TABLE PRIMARY KEY integer NOT NULL transaksi (
id
pid
serial
,
REFERENCES p e g a w a i ,
BAB 7.
timestamp NOT NULL DEFAULT varchar NOT NULL float NOT NULL float NOT NULL
4
tgl
5
ket
6
nominal
7
saldo
8
) ;
57
DATABASE
(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
UPDATE p e g a w a i SET
s a l d o = s a l d o + 3 0 0 0 0 0 0 WHERE i d = 1 ;
2 3
INSERT INTO
t r a n s a k s i ( pid , ket , nominal , s a l d o )
4
SELECT i d , ' GAJI
5
FROM p e g a w a i
6
WHERE i d = 1 ;
11 −2010 ' ,3000000 , s a l d o
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.
1
SELECT
i d FROM 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
id
|
pid
|
tgl
|
−−−−+−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−+ 1 | 1 | 2010 − 10 − 22 1 7 : 5 8 : 0 1 . 8 6 |
4 5 6 7
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
Listing 7.4: functransaksi.sql
REPLACE FUNCTION
p_pid p_ket
,
text ,
p_nominal
)
transaksi (
BAB 7.
integer
5
RETURNS
6
LANGUAGE p l p g s q l
7 8
AS
$$
DECLARE
9
rec
10
BEGIN
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
58
DATABASE
record ;
UPDATE SET WHERE INSERT INTO SELECT FROM WHERE SELECT INTO FROM ORDER BY DESC LIMIT pegawai
s a l d o = s a l d o + p_nominal i d = p_pid ; transaksi (
id ,
pid ,
nominal ,
tid ,
ket ,
p_pid ,
saldo )
p_nominal ,
p_ket ,
saldo
pegawai
i d = p_pid ;
id
rec
transaksi id
1;
RETURN r e c . i d ;
END $$
Restore script ini.
1 2
t o t a l i n d o=> \ i
functransaksi . sql
CREATE FUNCTION Misalkan ID pegawai 1 meminjam (cash bon) sebesar Rp200.000,- maka:
1
t o t a l i n d o=> SELECT
2
transaksi
3
−−−−−−−−−−−
4 5
t r a n s a k s i ( 1 , 'BON' , − 2 0 0 0 0 0 ) ;
2 (1
row ) Perhatikan nominal berisi nilai negatif yang artinya mengurangi saldo. Se-
karang lihat keseluruhan transaksi.
1 2
t o t a l i n d o=> SELECT id
|
pid
4
FROM t r a n s a k s i ; tgl
nominal
3
*
| |
|
ket
|
saldo
−−−−+−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−+−−−−−−−−−+−−−−−−−−− 1
|
1
|
2010 − 10 − 22
3000000
|
3000000
17:58:01.869139
|
GAJI 11 − 2010
|
BAB 7.
5
2
|
1
2010 − 10 − 22
|
− 200000 6
(2
59
DATABASE
|
18:20:08.845091
| BON
|
2800000
rows ) 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
CREATE OR integer float integer AS integer UPDATE SET WHERE
REPLACE FUNCTION
p_pid p_ket
17 18 19 20 21 22 23 24 25
transaksi (
,
text ,
p_nominal
)
RETURNS
LANGUAGE p l p g s q l $$
DECLARE tid
;
BEGIN
pegawai
s a l d o = s a l d o + p_nominal i d = p_pid ;
15 16
Listing 7.5: functransaksi.sql
tid = nextval ( ' transaksi_id_seq ' ) ;
INSERT INTO SELECT FROM WHERE id ,
pid ,
transaksi (
nominal ,
tid ,
ket ,
p_pid ,
saldo )
p_nominal ,
p_ket ,
saldo
pegawai i d = p_pid ;
RETURN t i d ;
END $$
Restore.
1 2
t o t a l i n d o=> \ i
transaksi . sql
CREATE FUNCTION Kini kita coba ID pegawai 5 bernama Ilham untuk transaksi.
1
t o t a l i n d o=> SELECT
2
transaksi
3
−−−−−−−−−−−
4 5
3 (1
row )
t r a n s a k s i ( 5 , ' GAJI
11 −2010 ' ,1500000) ;
BAB 7.
60
DATABASE
Lalu lihat hasilnya.
1
t o t a l i n d o=> SELECT
2
id
|
pid
|
|
ket
|
saldo
−−−−+−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−+−−−−−−−−−+−−−−−−−−−
4
1
|
1
|
2010 − 10 − 22
3000000
5
2
|
1
|
6
3
|
5
|
| |
GAJI 11 − 2010
17:58:01.869139
|
18:20:08.845091
| BON
18:41:45.309451
|
|
3000000 |
2800000
2010 − 10 − 22
1500000 (3
|
2010 − 10 − 22
− 200000
7
FROM t r a n s a k s i ; tgl
nominal
3
*
|
GAJI 11 − 2010
|
1500000
rows )
7.4.2
PL/Python
Penulisan fungsi PostgreSQL bisa juga dalam bahasa Python.
Biasanya saya
gunakan bahasa ini untuk algoritma yang tidak membutuhkan akses database. Pasanglah terlebih dahulu.
1
$
sudo
a p t −g e t
2
$
sudo
su
install
postgres
−c
p o s t g r e s q l −p l p y t h o n " createlang
−8.4
plpythonu
totalindo "
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
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
AS from
$$ uang
return
import
uang
uang ( n )
$$ ;
CREATE OR
REPLACE FUNCTION uang ( n
RETURNS t e x t
integer
)
LANGUAGE p l p y t h o n u
AS from
$$ uang
return
import
uang
uang ( n )
$$ ; Tidak seperti plpgsql, fungsi yang ditulis menggunakan plpythonu harus dibuat oleh user postgres.
BAB 7.
1
$
2
[ sudo ]
61
DATABASE
sudo
su
postgres
password
3
CREATE FUNCTION
4
CREATE FUNCTION
for
−c
" psql
totalindo
sugiana :
Lalu cobalah.
1
t o t a l i n d o=> SELECT uang ( 1 0 0 0 0 ) ;
2
uang
3
−−−−−−−−
4 5
10.000 (1
row )
−f
uang . s q l "
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
$
sudo
a p t −g e t
install
python − s q l a l c h e m y
2
$
sudo
a p t −g e t
install
python −p s y c o p g 2
Selanjutnya masuk ke modus interaktif untuk mencoba.
1
>>> i m p o r t
2
>>>
3
>>> db = s a . c r e a t e _ e n g i n e ( u r l )
4
>>> db . c o n n e c t ( )
5
<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
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 '
object
at
0 x 8 b 0 8 7 2 c>
Sampai di sini Python sudah terhubung ke PostgreSQL dan
uji coba login
berhasil dengan perintah connect(). Anda bisa lanjutkan dengan perintah query untuk melihat record tabel pegawai.
*
1
>>>
2
>>> q = db . e x e c u t e ( s q l )
3
>>>
4
...
s q l = "SELECT for
r
in
print
FROM p e g a w a i "
q: r
5
...
6
(1 ,
' Bummi Dwi
7
(2 ,
' Arief
8
(3 ,
' Cecep
9
(4 ,
' Nita
Putera ' ,
Setiadi ' , Zahrudin ' , Pandria ' ,
datetime . date (1985 ,
datetime . date (1972 , datetime . date (1972 ,
datetime . date (1976 ,
8,
5, 9,
17) ,
2) ,
6,
1) ,
19) ,
True )
True ) True ) False )
Lalu bagaimana kalau kita ingin melihat nilai berdasarkan nama eld-nya ? Untunglah variabel r di atas bisa dianggap sebagai dictionary yang keys-nya berisi nama eld dari query bersangkutan.
62
BAB 8.
63
PYTHON AKSES DATABASE
1
>>> q = db . e x e c u t e ( s q l )
2
>>>
3
...
4
...
5
1 Bummi Dwi
6
2
Arief
7
3
Cecep
8
4
Nita
for
r
in
print
q: r [ ' id ' ] ,
r [ ' nama ' ]
Putera
Setiadi Zahrudin Pandria
Bahkan eld id dan nama bisa dianggap variabel milik r:
1
>>> q = db . e x e c u t e ( s q l )
2
>>>
3
...
4
...
5
1 Bummi Dwi
6
2
Arief
7
3
Cecep
8
4
Nita
for
r
in
print
q: r . id ,
r . nama
Putera
Setiadi Zahrudin Pandria
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
>>> db . e x e c u t e ( s q l )
s q l = "UPDATE p e g a w a i SET nama = u p p e r ( nama ) "
3
<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
4
>>>
5
>>> q = db . e x e c u t e ( s q l )
6
>>>
7
...
8 9
s q l = "SELECT for
r
in
print
*
object
at
0 xb748a9ac>
FROM p e g a w a i "
q: r . nama
... BUMMI DWI PUTERA
10
ARIEF SETIADI
11
CECEP ZAHRUDIN
12
NITA PANDRIA
8.1
Active Record
Anda memiliki data pegawai dalam sebuah le yang berformat CSV yang isinya seperti ini: Listing 8.1: pegawai.csv ST; 1 9 8 5 − 0 8 − 1 7 ; t
1
1 ;BUMMI DWI PUTERA,
2
2 ; ARIEF SETIADI ; 1 9 7 2 − 0 5 − 0 2 ; t
3
3 ;CECEP ZAHRUDIN; 1 9 7 2 − 0 6 − 0 1 ; t
BAB 8.
64
PYTHON AKSES DATABASE
4
4 ; NITA PANDRIA; 1 9 7 6 − 0 9 − 1 9 ; f
5
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.
−U
1
$
2
Password
psql
3
psql
4
SSL
5
Type
ilham for
−h
user
localhost
totalindo
ilham :
(8.4.4) connection " help "
DHE−RSA−AES256−SHA,
( cipher :
for
bits :
256)
help .
6 7 8 9 10 11 12 13 14
t o t a l i n d o=> SELECT id
|
*
FROM p e g a w a i ;
nama
|
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
import
Listing 8.2: updatepegawai.py sqlalchemy
as
sa
3
url =
4
db = s a . c r e a t e _ e n g i n e ( u r l )
5
db . c o n n e c t ( )
' 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 '
6 7
filename =
8
f = open ( f i l e n a m e )
9 10
for
line pid ,
in
' pegawai . csv ' f . readlines () :
nama ,
tgl_lahir ,
pria =
line . strip () . s p l i t ( ' ; ' )
BAB 8.
11
pid = i n t ( pid )
12
p r i a = p r i a ==
13
s q l = "SELECT
14
q = db . e x e c u t e ( s q l )
if
15 16
65
PYTHON AKSES DATABASE
*
't '
# Agar boolean True / False
FROM p e g a w a i WHERE i d = %d " % p i d
q . rowcount : s q l = "UPDATE p e g a w a i SET " + \
17
"nama = '% s ' ,
18
" t g l _ l a h i r = '% s ' ,
19
" p r i a = %s
20
" + \ " + \
" + \
"WHERE i d = %d "
21
else
22 23
s q l = s q l % ( nama ,
tgl_lahir ,
pria ,
s q l = "INSERT INTO p e g a w a i
" + \
24
" ( id ,
25
"SELECT %d ,
'% s ' ,
'% s ' , %s "
s q l = s q l % ( pid ,
nama ,
tgl_lahir ,
26 27
pid )
: nama ,
tgl_lahir ,
pria )
" + \ pria )
db . e x e c u t e ( s q l ) Jalankan.
1
$
python
u p d a t e p e g a w a i . py
Kemudian ke psql, lihat tabel pegawai.
1
t o t a l i n d o=> SELECT
2 3 4 5 6 7 8 9
id
|
*
FROM p e g a w a i ORDER BY i d ;
nama
|
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 upda-
tepegawai.py tergolong sulit dibaca, dan ini bisa menyulitkan debugging (penelusuran kesalahan). Cara yang lebih nyaman adalah menggunakan teknik yang disebut
active record
.
Terlebih dahulu pasang paket Elixir.
1
$
sudo
a p t −g e t
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 ST; 1 9 8 5 − 0 8 − 1 7 ; t
1
1 ;BUMMI DWI PUTERA,
2
2 ; ARIEF SETIADI ; 1 9 7 2 − 0 5 − 0 2 ; t
3
3 ;CECEP ZAHRUDIN,
ST; 1 9 7 2 − 0 6 − 0 1 ; t
BAB 8.
66
PYTHON AKSES DATABASE
4
4 ; NITA PANDRIA; 1 9 7 6 − 0 9 − 1 9 ; f
5
5 ; ILHAM; 1 9 8 4 − 1 1 − 0 5 ; t
6
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
Listing 8.4: updatepegawai1.py
from
import
elixir
metadata ,
2 3 4 5
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 )
6 7
metadata . bind =
' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /
totalindo '
8
setup_all ()
9 10
filename =
11
f = open ( f i l e n a m e )
12 13
for
' pegawai . csv '
in
line pid ,
f . readlines () :
nama ,
tgl_lahir ,
pria =
line . strip () . s p l i t ( ' ; ' )
14
pid = i n t ( pid )
15
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
16
p . count ( ) :
17 18 19 20
r = p . one ( ) : r = Pegawai ( ) r . id = pid
21
r . nama = nama
22
r . tgl_lahir = tgl_lahir
23
r . p r i a = p r i a ==
24
s e s s i o n . commit ( )
't '
Jalankanlah.
1
$
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
*
FROM 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.
67
PYTHON AKSES DATABASE
7
4
|
NITA PANDRIA
|
1976 − 09 − 19
|
f
8
5
|
ILHAM
|
1984 − 11 − 05
|
t
| MIRANDA
|
1978 − 10 − 01
|
f
9 10
6 (6
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 ?
Buatlah le deletepega-
wai.py berikut ini.
1
from
import
elixir
setup_all ,
2 3 4 5 6 7 8 9 10 11 12 13 14
import class
Listing 8.5: deletepegawai.py Entity ,
using_options ,
metadata ,
\
session sys 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 )
if not
sys . argv [ 1 : ] :
s y s . e x i t ( ' Caranya :
try except
p y t h o n %s ' % s y s . a r g v [ 0 ] )
:
pid = i n t ( sys . argv [ 1 ] ) ValueError :
s y s . e x i t ( ' ID
pegawai
harus
angka ' )
15 16
metadata . bind =
' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /
totalindo '
17
setup_all ()
18 19 20 21
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 ( ) :
s y s . e x i t ( ' ID
p e g a w a i %d
tidak
ada ' % p i d )
22 23
r = p . one ( )
24
r . delete ()
25
print
26
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
ID
python
d e l e t e _ p e g a w a i . py
pegawai
4
sudah
dihapus
Periksa hasilnya di psql.
4
BAB 8.
1 2 3 4 5 6 7 8 9
t o t a l i n d o=> SELECT id
68
PYTHON AKSES DATABASE
|
*
FROM p e g a w a i ORDER BY i d ;
nama
|
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
from import class
elixir
import
Listing 8.6: selectpegawai.py Entity ,
using_options ,
metadata ,
setup_all
2 3 4 5
sys 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 )
6 7
metadata . bind =
' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /
totalindo '
8
setup_all ()
9
for
10 11
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
from import
Listing 8.7: selectpegawai2.py
elixir
import
setup_all
2 3
sys
Entity ,
using_options ,
metadata ,
BAB 8.
4 5
69
PYTHON AKSES DATABASE
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 )
6 7
metadata . bind =
' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /
totalindo '
8
setup_all ()
9
for
10 11
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
2 ARIEF SETIADI 1972 − 05 − 02
3
1 BUMMI DWI PUTERA,
4
3 CECEP ZAHRUDIN,
5
5 ILHAM 1984 − 11 − 05
6
6 MIRANDA 1978 − 10 − 01
python
s e l e c t _ p e g a w a i . py True
ST 1985 − 08 − 17
ST 1972 − 06 − 01
True
True
True False
Ingin yang lebih rapi ? Ubahlah seperti ini.
1 2 3 4 5
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 )
6 7
metadata . bind =
' p o s t g r e s : / / ilham : 1 2 3 4 @ l o c a l h o s t /
totalindo '
8
setup_all ()
9 10
jenis
= { True :
11
False :
' Pria ' , ' Wanita ' }
12 13 14 15
p = P e g a w a i . q u e r y . o r d e r _ b y ( ' nama ' )
for
16
in print r
p: str ( r . id ) . center (3) ,
r . nama . l j u s t ( 2 0 ) ,
17
r . tgl_lahir ,
18
jenis [ r . pria ]
\
\
\
Jalankan.
1
$
python
s e l e c t _ p e g a w a i . py
2
2
ARIEF SETIADI
3
1
BUMMI DWI PUTERA,
4
3
CECEP ZAHRUDIN,
1972 − 05 − 02
Pria
ST 1985 − 08 − 17
Pria
1972 − 06 − 01
Pria
ST
BAB 8.
70
PYTHON AKSES DATABASE
5
5
ILHAM
1984 − 11 − 05
Pria
6
6
MIRANDA
1978 − 10 − 01
Wanita
Elixir vs SQLAlchemy ? Sebenarnya Elixir bukanlah menggantikan SQLAlchemy, karena Elixir sendiri menggunakan engine SQLAlchemy.
Ini bisa terlihat pada penjelasan paket
python-elixir.
−s
1
$
2
Package :
dpkg
python − e l i x i r python − e l i x i r
3
Version :
0.7.1 −1
4
Depends :
p y t h o n (>=
5
Description :
8.2
python −s u p p o r t
2.4) ,
python − s q l a l c h e m y
(>=
Declarative
(>=
0.90.0) ,
0.4.0) Mapper
for
SQLAlchemy
Auto Commit
Masih ingat fungsi sql bernama transaksi() ? Kali ini akan kita buat transaksi perbankan secara interaktif. Buatlah transaksi.py berikut.
1 2 3
import import
Listing 8.9: transaksi.py sqlalchemy
as
sa
sys
4
url =
5
db = s a . c r e a t e _ e n g i n e ( u r l )
6
print try except
7 8 9 10 11 12
' 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 '
' Transaksi '
p i d = r a w _ i n p u t ( ' ID
pegawai :
')
:
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 )
13 14
s q l = "SELECT nama FROM p e g a w a i WHERE i d = %d " % p i d
15
q = db . e x e c u t e ( s q l )
16 17 18 19 20 21 22 23
if not print
q . rowcount :
s y s . e x i t ( ' ID ' Nama : ' ,
p e g a w a i %d
k e t = raw_input ( ' Keterangan :
if not
tidak
ada ' % p i d )
q . f e t c h o n e ( ) . nama ')
ket :
s y s . e x i t ( ' Keterangan
harus
diisi ')
BAB 8.
24 25 26 27 28
71
PYTHON AKSES DATABASE
n o m i n a l = r a w _ i n p u t ( ' Nominal :
try except
')
:
nominal =
f l o a t ( nominal )
ValueError :
s y s . e x i t ( ' Nominal %s
tidak
benar ' % nominal )
29 30
s q l = "SELECT
t r a n s a k s i (%d , ' % s ' , % . 2 f ) " % ( p i d ,
ket ,
nominal )
31 32
q = db . e x e c u t e ( s q l )
print
' Transaksi
berhasil ,
ID ' ,
q . fetchone () . transaksi
Jalankan.
1
$
2
Transaksi
python
t r a n s a k s i . py
3
ID
4
Nama :
5
Keterangan :
6
Nominal :
7
Transaksi
pegawai :
3
CECEP ZAHRUDIN,
ST
GAJI 11 − 2010
4500000 berhasil ,
ID
4
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
|
FROM t r a n s a k s i ;
tgl
|
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
transaction
. Ini bisa diartikan SQLAlchemy atau mungkin Psycopg2
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. Solusinya adalah memberitahukan SQLAlchemy untuk menjalankan
commit
. Ubahlah transaksi.py menjadi berikut ini.
1 2 3 4 5
import from import
Listing 8.10: transaksi.py sqlalchemy
as
sa
sqlalchemy . s q l . expression
url =
sys
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 '
auto
BAB 8.
6
db = s a . c r e a t e _ e n g i n e ( u r l )
7
print try except
8 9 10 11 12 13
72
PYTHON AKSES DATABASE
' Transaksi '
p i d = r a w _ i n p u t ( ' ID
pegawai :
')
:
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 )
14 15
s q l = "SELECT nama FROM p e g a w a i WHERE i d = %d " % p i d
16
q = db . e x e c u t e ( s q l )
17 18 19 20 21 22 23
if not print
q . rowcount :
s y s . e x i t ( ' ID ' Nama : ' ,
p e g a w a i %d
tidak
k e t = raw_input ( ' Keterangan :
if not
ada ' % p i d )
q . f e t c h o n e ( ) . nama ')
ket :
s y s . e x i t ( ' Keterangan
harus
diisi ')
24 25 26 27 28 29
n o m i n a l = r a w _ i n p u t ( ' Nominal :
try except
')
:
nominal =
f l o a t ( nominal )
ValueError :
s y s . e x i t ( ' Nominal %s
tidak
benar ' % nominal )
30 31
s q l = "SELECT
t r a n s a k s i (%d , ' % s ' , % . 2 f ) " % ( p i d ,
ket ,
nominal )
32 33
q = db . e x e c u t e ( t e x t ( s q l ,
print
' Transaksi
a u t o c o m m i t=True ) )
berhasil ,
ID ' ,
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.
73
PYTHON AKSES DATABASE
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 transak-
si. 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
IP publik di kantor pusat
outlet dapat mengakses database pusat.
agar synchronizer di
IP publik ini dapat dipesan ke ISP
(Internet Service Provider) bersangkutan. Setelah IP publik didapat, tambahkan baris berikut ini di
/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
$
sudo
2
*
Restarting
service
postgresql
PostgreSQL
−8.4
8.4
restart
database
server
Kini saatnya uji konektivitas database. Misalkan IP publiknya adalah 110.137.120.251, maka jalankan
1
$
psql
−U
di outlet
ilham
:
−h
110.137.120.251
totalindo
Di script Python gantilah localhost dengan IP publik tadi.
url = 'postgresql://ilham:[email protected]/totalindo' Mudah bukan ?
8.3.1
Aspek Keamanan
Tapi nanti dulu. Meski outlet itu hanya mendapat akses terbatas di database pusat, namun tetap saja ini masih menimbulkan resiko keamanan data. Alangkah baiknya menggunakan teknik sebaliknya, yaitu
pusat
synchronizer berada di
. Dengan kata lain pusat-lah yang membaca database outlet, sedangkan
outlet tidak mendapat hak akses apapun di database pusat. Lalu bagaimana host pusat mengakses host outlet ?
Apa perlu pesan IP
publik lagi ke ISP yang ada di outlet ? Idealnya ya, namun tidak harus. Kita bisa menerapkan VPN alias Virtual Private Network. Cukup pasang VPN server di pusat, dan pasang VPN client di outlet. Dengan VPN ini terbentuk jaringan baru dengan IP pusat 10.8.0.1,
BAB 8.
74
PYTHON AKSES DATABASE
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
tun0
ifconfig
tun0 Link
e n c a p : UNSPEC
HWaddr
00−00−00−00−00−00−00−00−00−00−00−00−00−00−00−00
3
inet
P−t −P : 1 0 . 8 . 0 . 5
addr : 1 0 . 8 . 0 . 6
Mask
:255.255.255.255
4
UP POINTOPOINT RUNNING NOARP MULTICAST :1500
5
MTU
Metric : 1
RX p a c k e t s : 0
errors :0
dropped : 0
overruns :0
errors :0
dropped : 0
overruns :0
frame : 0
6
TX p a c k e t s : 6 carrier :0
7
c o l l i s i o n s :0
8
RX b y t e s : 0
txqueuelen :100
(0.0
B)
TX b y t e s : 9 6 3
(963.0
B)
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 ? Tambahkan baris berikut ini di
di outlet
/etc/postgresql/8.4/main/pg_hba.conf
.
host all all 10.8.0.1/32 md5 Lalu restart daemon-nya.
1
$
sudo
2
*
Restarting
service
postgresql
PostgreSQL
−8.4
8.4
restart
database
server
Di pusat lakukan uji konektivitas dengan psql.
1
$
psql
−U
ilham
−h
10.8.0.6
totalindo
Jika sudah terhubung baik, saatnya membuat script synchronizer.
75
BAB 8.
PYTHON AKSES DATABASE
8.3.2
Tambah, Perbaharui, Hapus
Adalah sebuah tabel produk dengan struktur sebagai berikut:
1 2
create table id
produk (
primary key not null unique float not null
serial
3
nama name
4
harga
5
Listing 8.11: produk.sql
) ;
,
,
Isilah dengan beberapa record berikut ini:
1
INSERT INTO INSERT INTO INSERT INTO
SELECT SELECT SELECT
p r o d u k ( nama , h a r g a )
,10500;
2
p r o d u k ( nama , h a r g a )
,9250;
3
p r o d u k ( nama , h a r g a )
,14300;
' Jeruk
Pontianak '
' Salak
Pondoh '
' Mangga
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
import from from from
Listing 8.12: sync.py sqlalchemy
elixir
metadata
3 4 5
import
as
s q l a l c h e m y . orm
sa
Entity ,
using_options ,
import import
s q l a l c h e m y . schema
scoped_session ,
setup_all , sessionmaker
ThreadLocalMetaData
6 7
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.
8
76
PYTHON AKSES DATABASE
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 ' )
9 10
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 ) )
11
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 ) )
12 13
ms = m e t a d a t a
14
mt = T h r e a d L o c a l M e t a D a t a ( )
15 16
ms . b i n d = e s
17
mt . b i n d = e t
18 19 20 21
class
ProdukS ( 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 r o d u k ' , m e t a d a t a=ms ,
22 23 24
class
a u t o l o a d=True ,
s e s s i o n=s s )
ProdukT ( 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 r o d u k ' , m e t a d a t a=mt ,
a u t o l o a d=True ,
s e s s i o n=s t )
25 26 27 28 29 30 31 32
setup_all ()
# INSERT & UPDATE all_ps =
for
33
[]
in print try print except print ps
ProdukS . q u e r y . a l l ( ) : ps . id ,
p s . nama ,
34
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 ( )
35 36
' diubah '
s a . orm . e x c . NoResultFound ,
37 38
p t = ProdukT ( i d=p s . i d )
p t . nama = p s . nama
40
pt . harga = ps . harga
41
s t . commit ( )
42
a l l _ p s . append ( p s . i d )
44 45 46 47 48 49 50
e:
' ditambah '
39
43
ps . harga ,
:
# DELETE
for
pt
if
in not in print
ProdukT . q u e r y . a l l ( ) :
pt . i d
pt . id ,
pt . d e l e t e ( ) s t . commit ( )
all_ps : p t . nama ,
pt . harga ,
' dihapus '
BAB 8.
8.4
77
PYTHON AKSES DATABASE
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.
1
$
sudo
a p t −g e t
python −mysqldb
install
Lalu buatlah dbmigration.py berikut ini.
1 2 3 4 5 6
import from from from import
Listing 8.13: dbmigration.py sqlalchemy
as
sa
import import
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
ThreadLocalMetaData
scoped_session ,
*
7 8
dbs = s a . c r e a t e _ e n g i n e ( s y s . a r g v [ 1 ] )
9
dbt = s a . c r e a t e _ e n g i n e ( s y s . argv [ 2 ] )
sessionmaker
# source # target
10
dbs . c o n n e c t ( )
11
dbt . c o n n e c t ( )
12
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 ) )
13
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 ) )
14
metadata_s = m e t a d a t a
15
metadata_t = T h r e a d L o c a l M e t a D a t a ( )
16
metadata_s . b i n d = d b s
17
metadata_t . b i n d = d b t
18
for
19 20 21
tablename
class
in
dbs . table_names ( ) :
Source ( Entity ) :
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 ,
22
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 )
23 24
setup_all ()
25
class
26 27 28 29 30
Target ( Entity ) :
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
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
in
S o u r c e . mapper . c o l u m n s :
h a s _ f i e l d ( column . name ,
31
not
32
r e q u i r e d=
33
)
34 35 36
column . t y p e ,
p r i m a r y _ k e y=column . primary_key ,
s e t u p _ a l l ( True )
column . n u l l a b l e ,
BAB 8.
78
PYTHON AKSES DATABASE
Database
Paket
PostgreSQL
python-psycopg2
MySQL
python-mysqldb
SQLite
python-sqlite2
Interbase
python-kinterbasdb
MS SQL
python-pymssql
Tabel 8.1: Driver database
for
37 38
source
in
Source . query . a l l ( ) :
t a r g e t = Target ( )
39
t a r g e t . from_dict ( s o u r c e . to_dict ( ) )
40
s e s s i o n _ t . commit ( ) Contoh penggunaan:
1
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.
−s
1
$
2
..
3
Suggests :
dpkg
python − s q l a l c h e m y python −s q l a l c h e m y −doc ,
mysqldb (>= pysqlite2 1.1.7
−2)
(>= |
kinterbasdb
4
1 . 2 . 1 − p2 − 2) , 2.3.0
−1)
|
python − s q l i t e (>=
3.1.2
.. Perhatikan bagian Suggests.
python −p s y c o p g 2 ,
p y t h o n (>=
2.5)
|
python − p y s q l i t e 1 . 1 (>=
−0.3)
,
(>=
− 5 ) , python − python −p y m s s q l 1.0.1
python −
python −
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
eth0
3
ifconfig Link
encap : Ethernet
inet
addr : 1 9 2 . 1 6 8 . 1 1 . 1 1 1
HWaddr
0 0 : 2 5 : b3 : 7 7 : 3 e : 9 9
Bcast : 1 9 2 . 1 6 8 . 1 1 . 2 5 5
Mask : 2 5 5 . 2 5 5 . 2 5 5 . 0
4
UP BROADCAST MULTICAST
5
RX p a c k e t s : 0
6
TX p a c k e t s : 0
MTU: 1 5 0 0
Metric : 1
errors :0
dropped : 0
overruns :0
errors :0
dropped : 0
overruns :0
frame : 0 carrier :0
7
c o l l i s i o n s :0
8
RX b y t e s : 0
9
txqueuelen :1000
(0.0
B)
TX b y t e s : 0
(0.0
B)
Interrupt :17
10 11
Link
encap : L o c a l
12
inet
addr : 1 2 7 . 0 . 0 . 1
13
inet6
14
lo
addr :
Loopback
::1/128
UP LOOPBACK RUNNING
79
Mask : 2 5 5 . 0 . 0 . 0 Scope : Host MTU: 1 6 4 3 6
Metric : 1
BAB 9.
80
LINTAS SISTEM
15
RX p a c k e t s : 5 6 2
errors :0
dropped : 0
overruns :0
errors :0
dropped : 0
overruns :0
frame : 0
16
TX p a c k e t s : 5 6 2 carrier :0
17
c o l l i s i o n s :0
18
txqueuelen :0
RX b y t e s : 5 4 0 8 4 8
( 5 4 0 . 8 KB)
TX b y t e s : 5 4 0 8 4 8
( 5 4 0 . 8 KB)
19 20
Link
encap : Ethernet
21
wlan0
inet
addr : 1 9 2 . 1 6 8 . 1 . 4
22
inet6
HWaddr
0 0 : 1 f : 3 c : e0 : 6 6 : 5 6
Bcast : 1 9 2 . 1 6 8 . 1 . 2 5 5
Mask : 2 5 5 . 2 5 5 . 2 5 5 . 0 addr :
fe80 : : 2 1 f :3 c f f : fee0 :6656/64
Scope :
Link
23
UP BROADCAST RUNNING MULTICAST
MTU: 1 5 0 0
Metric : 1
24
RX p a c k e t s : 3 2 1 9
errors :0
dropped : 0
overruns :0
errors :0
dropped : 0
overruns :0
frame : 0
25
TX p a c k e t s : 3 6 0 8 carrier :0
26
c o l l i s i o n s :0
27
txqueuelen :1000
RX b y t e s : 1 5 8 6 2 9 7
( 1 . 5 MB)
TX b y t e s : 7 6 1 1 7 3
( 7 6 1 . 1 KB) Bagaimana memanggil command line di script Python dan mengolah hasilnya ? Gunakan modul commands. Buatlah netdev.py berikut ini.
1 2 3 4 5 6
import import def for
Listing 9.1: netdev.py commands re
netdev ( ) : r = {} 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
7
line
8
match = r e . c o m p i l e ( ' ( . * ) L i n k
9 10
if
=
line . strip () encap ( . * ) ' ) . s e a r c h (
line ) match : d e v i c e = match . g r o u p ( 1 ) . s t r i p ( )
11
r [ device ]
12
match = r e . c o m p i l e ( ' HWaddr
= {} ( . * ) ' ) . search ( l i n e
)
13 14 15 16
if continue
match : r [ d e v i c e ] [ ' addr ' ]
match = r e . c o m p i l e ( ' i n e t
= match . g r o u p ( 1 ) addr : ( . * ) ' ) . s e a r c h ( l i n e )
BAB 9.
if return
17 18 19 20 21 22 23
if
81
LINTAS SISTEM
match : r [ device ] [ ' ip ' ]
= match . g r o u p ( 1 ) . s p l i t ( ) [ 0 ]
r
__name__ ==
'__main__ ' :
nd = n e t d e v ( )
for
24 25
in print
dev
nd : dev ,
nd [ d e v ]
Jalankan.
1
$
2
wlan0
3
lo
4
eth0
python
n e t d e v . py
{ ' ip ' :
{ ' ip ' :
'192.168.1.4 ' ,
' ad dr ' :
' 0 0 : 1 f : 3 c : e0 : 6 6 : 5 6 ' }
'127.0.0.1 '}
{ ' ip ' :
'192.168.11.111 ' ,
' addr ' :
' 0 0 : 2 5 : b3 : 7 7 : 3 e
:99 '} 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.
9.3
82
LINTAS SISTEM
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 de-
ngan 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 ?
Atau me-
maksa pelanggan Anda itu untuk belajar Python dan membangun ulang sistem 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:
1
INSERT INTO
im . a n t r i a n ( p e n e r i m a ,
+628177654321 ' ,
' Selamat
pesan )
bergabung ' ;
SELECT
Lalu apa yang dilakukan oleh pelanggan Anda tadi ?
'
Ia perlu mengubah
source Borland Delphi-nya, yaitu pada fungsi pengiriman SMS menggunakan koneksi ke database PostgreSQL yang ada di server SMS gateway.
Biasanya
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.
1
SELECT
*
FROM im . a n t r i a n WHERE NOT k i r i m ;
Mudah bukan ? Lebih lanjut mengenai SMS gateway bisa lihat halaman114.
BAB 9.
83
LINTAS SISTEM
Lintas sistem ini tergolong bisa beda komputer, karena baik PostgreSQL maupun MySQL dapat diakses melalui jalur network.
9.4
XMLRPC
Database sebagai perantara bisa jadi ditolak dengan berbagai alasan.
Salah
satunya adalah aspek tanggung jawab, atau biasa disebut aspek keamanan (security). Misalkan dalam sistem perbankan. Sistem utama sebuah bank dibangun oleh PT Software Aman Banget (SAB). Namanya juga sistem utama, ia memuat data penting nasabah serta data yang menyangkut sistem. Suatu saat bank ini ingin go Internet dimana nasabah dapat melakukan transaksi melalui browser Firefox. Berarti dibutuhkan web developer dan dipilihlah PT Web Perkasa (WP) untuk mengembangkannya.
Karena mengembangkan
Internet banking, berarti ada proses login untuk nasabah. Ini berarti ada algoritma pemeriksaan username dan password. Sampai disini tidaklah menjadi masalah karena username dan password web bisa berada di web server yang dibangun oleh WP. Tibalah saatnya nasabah ingin melihat mutasi transaksinya.
Apakah WP
melakukan SELECT tabel ke sistem utama bank tersebut yang dibangun oleh SAB ? Mayoritas jawabannya tidak. Mengapa ? SELECT berarti kita bicara koneksi ke database.
Itu artinya WP meng-
etahui 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.
1 2
def
tambah ( a ,
return
b) :
a + b
XMLRPC bisa juga disebut sebagai
web service
karena dokumen XML ter-
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.
BAB 9.
84
LINTAS SISTEM
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
Listing 9.2: serverbank.py
from import
SimpleXMLRPCServer
class
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
7
def
8 9 10 11
verify_request ( self ,
request ,
client_address ) :
server . ip_client = client_address [ 0 ]
print return
server . ip_client
in
server . ip_client
[ ' 127.0.0.1 ' , '
192.168.0.1 ' , ' 192.168.1.5 ' ]
12 13 14 15 16
def
r e s p ( code ,
return
msg ) :
{ ' kode ' :
code ,
' pesan ' :
msg }
17 18 19 20 21
class def
Agent :
22
_dispatch ( s e l f ,
print try except return return
method ,
method ,
params =() ) :
params
:
23
func = g e t a t t r ( s e l f ,
24
' e x p o r t _ ' + method )
AttributeError :
25
r e s p ( −1 ,
' F u n g s i %s
tidak
dikenal ' %
method )
f u n c ( * params )
26 27 28 29
def
export_mutasi ( s e l f , s q l = "SELECT
p) :
t g l : : date ,
ket ,
nominal ,
saldo
" +
\
30
"FROM t r a n s a k s i WHERE p i d = %d
31
"AND t g l : : d a t e BETWEEN '% s ' AND '% s '
32
"ORDER BY i d "
33
s q l = s q l % (p [ ' pid ' ] ,
34
q = db . e x e c u t e ( s q l )
35
m =
36 37
for
[] r
in
p [ ' awal ' ] ,
" + \ " + \
p [ ' akhir ' ] )
q:
m. append ( [ s t r ( r . t g l ) , saldo ] )
r . ket ,
r . nominal ,
r.
BAB 9.
85
LINTAS SISTEM
38
r = r e s p ( 0 , 'OK ' )
39
return
r [ ' mutasi ' ]
40 41
= m
r
42 43 44
url =
45
db = s a . c r e a t e _ e n g i n e ( u r l )
' 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 '
46 47
p o r t = 9303
48
server = Server (( ' 0 . 0 . 0 . 0 ' , port ) )
49
server . register_introspection_functions ()
50
print
51 52
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
HTTP XMLRPC s e r v e r
python
s e r v e r b a n k . py port
9303
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
Listing 9.3: clientbank.py
import
xmlrpclib
3
url =
4
remote = x m l r p c l i b . ServerProxy ( u r l )
5
d = { ' pid ' :
' http : / / 1 2 7 . 0 . 0 . 1 : 9 3 0 3 ' 1,
6
' awal ' :
7
' akhir ' :
8 9 10
if
r [ ' k o d e ' ] == 0 :
for
12 14 15
' 20101023 ' }
r = remote . mutasi ( d )
11 13
' 20101022 ' ,
else
tgl ,
print
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)
:
print
r [ ' pesan ' ]
Bukalah konsole lainnya, lalu jalankan le ini.
1
$
2
2010 − 10 − 22 GAJI 11 − 2010
3000000
3000000
3
2010 − 10 − 22 BON
− 200000
2800000
python
c l i e n t b a n k . py
\
BAB 9.
86
LINTAS SISTEM
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
Unduh xmlrpc.inc dari http://phpxmlrpc.sourceforge.net. xmlrpc-2.2.2.tar.gz.
Carilah.
File itu berada di
Versi lain sepertinya juga tidak masalah.
buatlah le clientbank.php. Listing 9.4: clientbank.php
1 2 3 4 5
include array
( ' xmlrpc . i n c ' ) ;
$params = new
xmlrpcval (
(
' pid '
=> new
xmlrpcval (1 , ' i n t ' ) ,
6
' awal '
=> new
xmlrpcval ( ' 20101022 ' ) ,
7
' a k h i r ' => new
8
xmlrpcval ( ' 20101023 ' )
) ,
9
' struct '
10
) ;
11
$ f = new
xmlrpcmsg ( ' m u t a s i ' ,
12
$ c = new
xmlrpc_client ( ' / ' ,
13
$ r = $c −>s e n d ( $ f ) ;
14
$v = $ r −>v a l u e ( ) ;
15 16 17
if
}
18
( $ r −>f a u l t C o d e ( ) )
print − else − if foreach
array
( $params ) ) ;
' 127.0.0.1 ' ,
9303) ;
{
$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 )
19
{
( $ r e s p [ ' m u t a s i ' ]−> s c a l a r v a l ( )
20
$ f = $m−> s c a l a r v a l ( ) ;
21 22
$tgl
= $ f [0] − > s c a l a r v a l ( ) ;
23
$ket
= $ f [1] − > s c a l a r v a l ( ) ;
24
$ n o m i n a l = $ f [2] − > s c a l a r v a l ( ) ;
25
$saldo
26
= $ f [3] − > s c a l a r v a l ( ) ;
print str_pad str_pad str_pad else print
$tgl . "
27 28 29 30
".
( $ket , 2 0 ) . "
".
( $nominal , 1 0 ) . "
".
( $ s a l d o , 1 0 ) . " \n" ;
}
31
}
{
$ r e s p [ ' p e s a n ' ]−> s c a l a r v a l ( ) ;
32 33
}
34
}
35
?>
as
$ i=>$m)
{
Lalu
BAB 9.
87
LINTAS SISTEM
Karena script ini akan kita jalankan sebagai command line, maka pasang dulu paket terkait.
1
$
sudo
a p t −g e t
php5− c l i
install
Lalu jalankan.
1
$
2
2010 − 10 − 22 GAJI 11 − 2010
3000000
3000000
3
2010 − 10 − 22 BON
− 200000
2800000
php
c l i e n t _ b a n k . php
9.4.2 Drupal
Drupal 1 juga ditulis dengan PHP. Ia sudah memuat function xmlrpc, sehingga
Anda tidak perlu memasang modul tambahan.
Cara penulisannya juga lebih
mudah. Contoh berikut ini untuk Drupal 6. Buatlah le sites/all/modules/pegawai/pegawai.module berikut. Listing 9.5: pegawai.module
1
2
function
pegawai_menu ( )
array
{
3
$items =
4
$ i t e m s [ ' p e g a w a i / t r x/%/%/% ' ]
() ;
5
' t i t l e ' =>
6
' page
c a l l b a c k ' =>
7
' page
a r g u m e n t s ' =>
8
' access
9
(
' pegawai_trx ' ,
array
c a l l b a c k ' =>
(2 ,3 ,4) ,
' user_access ' ,
' t y p e ' => MENU_CALLBACK,
10
) ;
11 12
array
=
' Transaksi ' ,
return
$items ;
}
13 14
function
pegawai_trx ( $pid ,
15
$url =
16
$p =
17
array
18 19 20 21
$akhir )
{
intval
( $pid ) ,
' a w a l ' => $ a w a l , ' a k h i r ' => $ a k h i r ) ;
if
( ! $r [ ' mutasi ' ] ) return
}
23
$c =
24
foreach
26
( ' p i d ' =>
$r = xmlrpc ( $ u r l ,
22
25
$awal ,
' http : / / 1 2 7 . 0 . 0 . 1 : 9 3 0 3 ' ;
' mutasi ' ,
$p ) ;
{
drupal_set_message ( ' Tidak
ada
data ' ) ;
'T a n g g a l | K e t e r a n g a n | ' . 'Nominal | S a l d o | ' ; ( $r [ ' mutasi ' ]
$tgl = $f [ 0 ] ;
1 http://drupal.org
as
$ i=>$ f )
{
BAB 9.
88
LINTAS SISTEM
27
$ket = $f [ 1 ] ;
28
$nominal = $ f [ 2 ] ;
29
$saldo = $f [ 3 ] ;
30
$c
.=
' ' . $ t g l . ' | ' ;
31
$c
.=
' ' . $ k e t . ' | ' ;
32
$c
.=
'
a l i g n =" r i g h t "> ' . format_number ( $ n o m i n a l
) . ' ' ;
33
$c
.=
'
a l i g n =" r i g h t "> ' . format_number ( $ s a l d o ) .
' ' ;
34
$c
35 36 37
.=
' t r > ' ;
} return
$c ;
} Juga buat sites/all/modules/pegawai/pegawai.info. Listing 9.6: pegawai.info
1
name = P e g a w a i
2
d e s c r i p t i o n = Data
3
core = 6. x
4
v e r s i o n = " 6 . x −1.0"
pegawai
Setelah login di browser, masuklah ke menu admin/build/modules. Centang modul pegawai dan klik Save. Lalu masukkan URL di browser seperti
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
dependencies
. Misalkan
paket python-uang tadi membutuhkan python-sqlalchemy, maka pada saat
1
$
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 bi-
Tidak 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.
90
PENGEMASAN
Mulailah membuat direktori kerja, yaitu /usr/local/src/python-uang.
1
$
sudo
−p
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
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.
prerm
script yang dijalankan saat penghapusan berlangsung, yaitu saat apt-
get remove atau dpkg -r. Script ini juga harus executable.
conles
berisi daftar nama le kongurasi yang digunakan paket ini. Misal-
nya berisi /etc/uang.conf. File kongurasi ini berisi format negara yang digunakan oleh fungsi uang().
Jadi tidak hard-code menggunakan loca-
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-
Kemudian dibuatlah versi 0.2 yang juga
di dalam sudah terpasang
versi 0.2 tidak ada perubahan
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 python −uang
1
Package :
2
Priority :
3
Section :
4
Maintainer :
5
Architecture :
6
Version :
0.1
7
Depends :
python − c e n t r a l
8
Python− V e r s i o n :
9
Description :
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)
all
Format
uang
Lanjutkan dengan membuat direktori modulnya.
BAB 10.
1
$
cd
2
$
sudo
91
PENGEMASAN
/ usr / l o c a l / src mkdir
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
Perhatikanlah direktori setelah /usr/local/src/python-uang.
Semua direk-
tori tersebut nantinya akan dipasang di root directory ( / ), kecuali direktori DEBIAN. Lalu mengapa menggunakan /usr/share ? Padahal sebelumnya kita meletakkan uang.py di /usr/local ?
Direktori /usr/local yang kita gunakan sebe-
lumnya 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.
1
$
sudo
/ 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
cp
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. Listing 10.2: usr/share/pycentral/python-uang/site-packages/uang.py
1 2 3 4
import from from
locale
types
import
ConfigParser
IntType
import
ConfigParser
5
conf = ConfigParser ()
6
c o n f . r e a d ( ' / e t c / uang . c o n f ' )
7 8
l o c a l e . s e t l o c a l e ( l o c a l e . LC_ALL,
conf . get ( ' default ' , '
locale '))
9 10
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 ' )
11
def if
12 13 14 15 16 17 18 19 20 21 22 23 24 25
ribu ( nilai , pecahan
if else return def if return
is
p e c a h a n=None ) : None :
t y p e ( n i l a i ) == I n t T y p e :
pecahan = 0 :
p e c a h a n = DECIMAL l o c a l e . f o r m a t ( '%%.%d f ' % p e c a h a n ,
uang ( n i l a i , tanda
is
tanda =
p e c a h a n=None ,
nilai ,
True )
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 ' ]
'%s%s ' % ( t a n d a ,
ribu ( nilai ,
pecahan ) )
BAB 10.
26 27 28
if
29 30 31
92
PENGEMASAN
__name__ ==
'__main__ ' :
n = 10000.5
print print print
uang ( n ) uang ( i n t ( n ) ) uang ( n , 3 )
Buat direktori etc.
1
$
sudo
mkdir
−p
python −uang / e t c
Selanjutnya buat python-uang/etc/uang.conf. Listing 10.3: etc/uang.conf
1
[ default ] = id_ID . UTF−8
2
locale
3
decimal = 2 Lalu buat python-uang/DEBIAN/conles berisi satu baris berikut. Listing 10.4: DEBIAN/conles
1
/ 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
#!/ b i n / s h
2
pycentral
pkginstall
python −uang
Juga python-uang/DEBIAN/prerm. Listing 10.6: DEBIAN/prerm
1
#!/ b i n / s h
2
pycentral
pkgremove
python −uang
Buat keduanya executable.
1
$
sudo
chmod
755
python −uang /DEBIAN/ p o s t i n s t
2
$
sudo
chmod
755
python −uang /DEBIAN/ prerm
Pastikan semua le dimiliki root.
1
$
sudo
chown
−R
root . root
python −uang
Lihat lagi apa yang sudah dibuat. python −uang
1
$
2
python −uang /
3
python −uang /DEBIAN
4
python −uang /DEBIAN/ c o n t r o l
5
python −uang /DEBIAN/ prerm
find
BAB 10.
93
PENGEMASAN
6
python −uang /DEBIAN/ c o n f f i l e s
7
python −uang /DEBIAN/ p o s t i n s t
8
python −uang / u s r
9
python −uang / u s r / s h a r e
10
python −uang / u s r / s h a r e / p y c e n t r a l
11
python −uang / u s r / s h a r e / p y c e n t r a l / python −uang
12
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
13
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
14
python −uang / e t c
15
python −uang / e t c / uang . c o n f
−p a c k a g e s −p a c k a g e s
/ uang . py
Kemas. dpkg−deb
1
$
2
dpkg−deb :
3
dpkg−deb :
sudo
−− b u i l d
peringatan :
python −uang
u s e r −d e f i n e d
contains
membuat
.
' python −uang /DEBIAN/ c o n t r o l '
paket
field
' Python−V e r s i o n '
` python −uang '
di
dalam
` . / python −
uang_0 . 1 _ a l l . deb ' .
4
dpkg−deb :
peringatan :
control
ignoring
1
warnings
about
the
file (s)
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.
−i
python −uang_0 . 1 _ a l l . deb
1
$
2
Memilih
paket
3
( Sedang
membaca
4
S e d a n g membuka
sudo
dpkg
telah
Sedang
6
Processing
basis
yang
data
sebelumnya
...213458
tidak
berkas
dan
dipilih . direktori
terpasang . )
_ a l l . deb )
5
python −uang
python −uang
paket
( dari
python −uang_0 . 1
... python −uang
menyetel
triggers
for
(0.1)
...
python − c e n t r a l
...
Untuk menghindari kerancuan karena ada dua modul uang yang terpasang, sebaiknya buang yang ada di /usr/local.
1
$
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
Python
3
[GCC
4
Type
python 2.6.5
4.4.3]
( r265 :79063 , on
" help " ,
" copyright " ,
information .
5
>>> f r o m
uang
Apr
16
2010 ,
13:09:56)
linux2
import
uang
" credits "
or
"license"
for
more
BAB 10.
6 7
94
PENGEMASAN
>>> uang ( 1 0 0 0 0 ) '10.000 ' Ujian terakhir hapuslah paket ini untuk memastikan script DEBIAN/prerm berfungsi dengan baik.
1
$
sudo
a p t −g e t
remove
python −uang
2
Sedang
membaca
daftar
paket . . .
3
Membangun pohon
4
Membaca
5
Paket 0
dimutakhirkan , tidak
8
yang
tersedia . . .
Selesai
akan DIHAPUS :
python −uang
6 7
informasi
berikut
Selesai
ketergantungan
Setelah
akan
operasi
0
baru
terinstal ,
1
akan
dihapus
dan
192
dimutakhirkan . ini ,
0B r u a n g
kosong
harddisk
akan
digunakan .
9 10
Anda
ingin
( Sedang
telah
11
melanjutkan
membaca
basis
[ Y/ t ] ? data
...213460
berkas
dan
direktori
terpasang . ) python −uang
Sedang
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.
1
$
sudo
a p t −g e t
install
dpkg−d e v
Letakkan paketnya di tempat yang sudah ditentukan web server Apache.
1
$
sudo
mkdir
2
$
sudo
cp
/ v a r /www/ deb
/ u s r / l o c a l / s r c / python −uang_0 . 1 _ a l l . deb
/ v a r /www
/ 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
−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
rm
all
.
/ dev / n u l l > P a c k a g e s
Setiap ada paket baru jalankan le ini.
1
$
cd
2
$
sudo
/ v a r /www/ deb sh
update− l i s t . s h
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.
95
PENGEMASAN
Listing 10.8: /etc/apt/sources.list.d/custom.list
1
deb
h t t p : / / 1 9 2 . 1 6 8 . 0 . 1 / deb
./
Perbaharui daftar paket.
1
$
sudo
a p t −g e t
update
Pasang paket yang dimaksud.
1
$
sudo
10.3
a p t −g e t
install
python −uang
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.
1
$
sudo
a p t −g e t
install
s q u a s h f s −t o o l s
mudah
.
mkisofs
Buat le conf. Listing 10.9: remaster/conf
1
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
2
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 "
3
LABEL=" BlankOn
" 6 SOHO"
Sesuaikanlah ketiganya. Kemudian buat le 1-extract.sh: Listing 10.10: remaster/1-extract.sh
1
.
./ conf
2
mkdir
3
mount
4
rsync
−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
'
−a
/tmp/ cdrom /
cdrom /
5
mount
−o
loop
squashfs
−a
−t
squashfs
/tmp/ cdrom / c a s p e r / f i l e s y s t e m .
/tmp/ r o o t
6
cp
7
umount
/tmp/ r o o t
8
umount
/tmp/ cdrom
/tmp/ r o o t
.
Lalu buat le 2-chroot.sh. Listing 10.11: remaster/2-chroot.sh
1
cp
2
chroot
root
mount
3
chroot
root
mount
/ etc / resolv . conf
−t −t
root / etc proc sysfs
none none
/ proc / sys
BAB 10.
−f
4
chroot
root
rm
5
chroot
root
a p t −g e t
6
chroot
root
umount
7
chroot
root
umount
8
chroot
root
rm
9
rm
−r f
96
PENGEMASAN
root / etc / resolv . conf clean sys proc /tmp / *
−r f
/ root / . bash_history
cdrom / p r o g r a m s
10
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
11
chroot
dpkg−q u e r y
root
−W −−s h o w f o r m a t = ' $ { P a c k a g e }
${
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
12
cp
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 −d e s k t o p
13
sed −
ie
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 −
' / u b i q u i t y /d '
desktop
Terakhir buat le 3-pack.sh. Listing 10.12: remaster/3-pack.sh
1
.
2
rm
3
awal =` date `
4
cd
5 6 7
8 9 10 11
./ conf
−f
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
mksquashfs
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
cdrom
− 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 . −no−emul−b 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 .
find
.
mkisofs
echo echo echo echo
cat
" Awal
$awal "
" Akhir
" ` date `
" Source
" ` ls
" Target
" ` ls
−l −l
$SOURCE
|
awk awk
. . / $TARGET
'{ p r i n t
|
$5 } ' `
'{ p r i n t
$5 } ' `
Jalankan script pertama.
1
$
sudo
s h 1− e x t r a c t . s h
Dia akan mengekstrak le iso menjadi root directory.
Kemudian jalankan
script kedua:
1
$
sudo
s h 2− c h r o o t . s h
Script ini untuk masuk ke sistem Linux yang lain, yaitu root directory tadi.
1
r o o t @ l a p t o p :/# Di sini Anda bisa melakukan perubahan sebagaimana biasa. Contoh:
1
r o o t @ l a p t o p :/#
cd
2
r o o t @ l a p t o p :/#
wget
3
r o o t @ l a p t o p :/#
a p t −g e t
update
4
r o o t @ l a p t o p :/#
a p t −g e t
install
server
squid
/ e t c / apt / s o u r c e s . l i s t . d
vim
http : / / debian . rab . co . i d / rab . l i s t i n t e r n e t −s h a r i n g
dhcp3 −
BAB 10.
97
PENGEMASAN
Kalau sudah selesai keluarlah dari chroot dengan menekan Ctrl-D atau ketik exit:
1
r o o t @ l a p t o p :/#
2
exit
3
sugiana@laptop :~ $
exit
Root directory kini telah berisi aplikasi yang dibutuhkan. Saatnya membuat iso dengan menjalankan script ketiga.
1
$
s u d o 3− p a c k . s h Tunggu sekitar 5 menit.
1
Parallel
mksquashfs :
2
Creating
4.0
squashfs ,
3
Using
filesystem block
size
on
2
processors
cdrom / c a s p e r / f i l e s y s t e m .
131072.
[==========================|
]
15063/84258
17%
Terakhir script akan memberikan informasi ukuran iso yang dihasilkan berikut iso aslinya.
1
357433
2
Awal
Sen Agu
3
Akhir
Sen Agu 2
4
Source
731869184
5
Target
732022784
extents
written 2
( 6 9 8 MB)
0 9 : 4 5 : 3 9 WIT 2 0 1 0 0 9 : 5 0 : 5 2 WIT 2 0 1 0
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.
1
$
sudo
s h 2− c h r o o t . s h
Setelah selesai jalankan lagi script ketiga:
1
$
sudo
s h 3− p a c k . s h
1
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.
1
$
a p t −g e t
sudo
install
python −q t 4
Juga dokumentasinya, meski tidak wajib.
1
$
a p t −g e t
sudo
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
import from from class def
Listing 11.1: hello.py sys
PyQt4
import
PyQt4 . QtGui
Qt
import
*
FormUtama ( QMainWindow ) : __init__ ( s e l f ) : QMainWindow . __init__ ( s e l f ) s e l f . setWindowTitle ( ' H e l l o
9 10
98
world ' )
BAB 11.
GRAPHICAL USER INTERFACE
99
Gambar 11.1: Hello world
11
app = Qt . Q A p p l i c a t i o n ( s y s . a r g v )
12
fm = FormUtama ( )
13
fm . show ( )
14
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.
Mari kita
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
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__().
Fungsi ini merupakan fungsi khusus
yang dijalankan saat pembentukan sehingga disebut sebagai constructor. Saat pembentukan ? Ya, perhatikan baris 12
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
Mari kita buat form yang lebih lengkap.
Form ini untuk memasukkan data
pegawai, terinspirasi dari tabel pegawai yang pernah kita buat. Namun disini belum menggunakan database, semuanya tersimpan sementara saja.
BAB 11.
1 2 3 4 5 6 7 8 9
import from from from class def
101
GRAPHICAL USER INTERFACE
Listing 11.2: gui/pegawai.py sys
PyQt4
import
PyQt4 . QtGui
PyQt4 . QtCore
Qt
import * import
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 ' )
10
s e l f . r e s i z e (550 ,550)
11
s e l f . l a b e l N a m a = QLabel ( s e l f )
12
s e l f . l a b e l A l a m a t = QLabel ( s e l f )
13
s e l f . l a b e l T g l L a h i r = QLabel ( " t g l
14
self . labelJenis
15
s e l f . editNama = Q L i n e E d i t ( s e l f )
16
s e l f . e d i t A l a m a t = QTextEdit ( s e l f )
17
s e l f . e d i t T g l L a h i r = QDateEdit ( s e l f )
18
s e l f . e d i t J e n i s = QComboBox ( s e l f )
19
s e l f . b u t t o n S i m p a n = QPushButton ( s e l f )
20
s e l f . l i s t P e g a w a i = QTableWidget ( s e l f )
21
s e l f . l a b e l N a m a . s e t T e x t ( ' Nama ' )
22 23 24
lahir " ,
self )
= QLabel ( s e l f )
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 . labelTglLahir . setText ( ' Tgl Lahir ') s e l f . l a b e l J e n i s . setText ( ' Jenis
K el a mi n ' )
25
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−MM −yyyy ' )
26
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 ) )
27
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 ' ] )
28
s e l f . b u t t o n S i m p a n . s e t T e x t ( ' Simpan ' )
29
s e l f . l i s t P e g a w a i . setColumnCount ( 5 )
30
s e l f . listPegawai . setHorizontalHeaderLabels ( [
31 32 33 34 35
' ID ' , ' Nama ' , ' Alamat ' , ' T g l
L a h i r ' , ' K el a mi n ' ] )
s e l f . l i s t P e g a w a i . setColumnWidth ( 0 , 2 0 ) s e l f . l i s t P e g a w a i . setColumnWidth ( 1 , 1 5 0 )
#s e l f . editNama . resize (200 ,30)
s e l f . editAlamat . r e s i z e (200 ,100)
36
s e l f . listPegawai . r e s i z e (500 ,200)
37
s e l f . l a b e l N a m a . move ( 1 0 , 1 0 )
38
s e l f . l a b e l A l a m a t . move ( 1 0 , 4 0 )
39
s e l f . l a b e l T g l L a h i r . move ( 1 0 , 1 4 0 )
40 41 42
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 . move(100 ,10)
s e l f . editNama . s e t G e o m e t r y ( 1 0 0 , 1 0 ,
43
s e l f . e d i t A l a m a t . move ( 1 0 0 , 4 0 )
44
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.
102
GRAPHICAL USER INTERFACE
45
s e l f . e d i t J e n i s . move ( 1 0 0 , 1 7 0 )
46
s e l f . b u t t o n S i m p a n . move ( 1 0 0 , 2 0 0 )
47
s e l f . l i s t P e g a w a i . move ( 1 0 , 3 0 0 )
48
s e l f . c o n n e c t ( s e l f . buttonSimpan , ') ,
49 50 51
def
52 53
closeEvent ( s e l f , s =
for
54
' '
in
row r =
for
55
SIGNAL ( ' c l i c k e d ( )
s e l f . simpan ) event ) :
r a n g e ( s e l f . l i s t P e g a w a i . rowCount ( ) ) :
' '
col () ) : v =
in
r a n g e ( s e l f . l i s t P e g a w a i . columnCount
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 () )
56
r += v +
' , '
#r = r + v + ' , '
57 58
s += r . s t r i p ( ' , ' ) +
59
f = o p e n ( ' p e g a w a i . c s v ' , 'w ' )
60
f . write ( s )
' \n '
61
f . close ()
62
QMainWindow . c l o s e E v e n t ( s e l f ,
63 64 65
def
# chr (13) event )
simpan ( s e l f ) : row =
s e l f . l i s t P e g a w a i . rowCount ( )
66
p i d = row+1
67
s e l f . l i s t P e g a w a i . setRowCount ( p i d )
68
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 (
69
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 (
70
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 (
71
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 (
72
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 (
73
s e l f . editNama . c l e a r ( )
74
s e l f . editAlamat . c l e a r ( )
75
s e l f . e d i t J e n i s . setCurrentIndex (0)
76
s e l f . editNama . s e t F o c u s ( )
s t r ( pid ) ) ) s e l f . editNama . t e x t ( ) ) ) s e l f . editAlamat . toPlainText ( ) ) ) s e l f . editTglLahir . text () ) ) s e l f . e d i t J e n i s . currentText () ) )
77 78 79
app = Qt . Q A p p l i c a t i o n ( s y s . a r g v )
80
fm = FormUtama ( )
81
fm . show ( )
82
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.
Berikut ini
ulasan masing-masing class tersebut.
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
ini menyimpan data pegawai ke QTableWidget yang ada di bawahnya.
QTableWidget
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
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.
104
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
1 Jeruk
2
2 Mangga
3
3 Pisang
34
9000
7
10000
8000
Pembuat le itu memastikan data barang tersimpan dengan
tetap
.
lebar kolom
Kolom kode barang 3 karakter, nama barang 10 karakter, stok 2 ka-
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.
Sayang sekali hal ini tidak dapat dilakukan.
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.
Idealnya pembuat le itu mengubah programnya
agar kalau stok kosong diberi angka 0.
Namun posisi kita sedang tidak bisa
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.
105
BAB 12.
OBJECT ORIENTED PROGRAMMING
106
Mulailah membuat script barang1.py.
1 2
Listing 12.2: barang1.py
import
sys
3
f i l e n a m e = sys . argv [ 1 ]
4
for
5 6
f = open ( f i l e n a m e )
in
line
f . readlines () :
kode =
line [ : 3 ] . strip ()
7
nama =
line [3:3+10]. strip ()
8
stok =
l i n e [3+10:3+10+2]. s t r i p ( )
9
print
harga =
10 11
l i n e [3+10+2:]. s t r i p ()
[ kode ,
nama ,
stok ,
harga ]
f . close ()
Jalankan.
1
$
2
[ '1 ' ,
' Jeruk ' ,
3
[ '2 ' ,
' Mangga ' ,
' ' ,
4
[ '3 ' ,
' Pisang ' ,
'7 ' ,
python
b a r a n g 1 . py
barang . t x t
'34 ' ,
'9000 '] '8000 '] '10000 ']
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
def
3
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
4 5
[]
line
in
kode =
f . readlines () : line [ : 3 ] . strip ()
6
nama =
line [3:3+10]. strip ()
7
stok =
l i n e [3+10:3+10+2]. s t r i p ( )
8
harga =
9
row =
10
r o w s . append ( row )
11
f . close ()
return
12 13 14 15
l i n e [3+10+2:]. s t r i p ()
[ kode , nama , s t o k , h a r g a ]
if
rows
__name__ ==
'__main__ ' :
BAB 12.
OBJECT ORIENTED PROGRAMMING
import for in print
16
107
sys
17
f i l e n a m e = sys . argv [ 1 ]
18
row
19
barang ( f i l e n a m e ) : row
Jalankan.
1
$
2
[ '1 ' ,
' Jeruk ' ,
3
[ '2 ' ,
' Mangga ' ,
' ' ,
4
[ '3 ' ,
' Pisang ' ,
'7 ' ,
python
b a r a n g 2 . py '34 ' ,
barang . t x t '9000 '] '8000 '] '10000 ']
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
setiap
. Kalau barang.txt menggunakan lebar kolom
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 berbeda. Karena sudah bersifat umum sebaiknya nama fungsi dan nama lenya pun umum. Buatlah xtable.py berikut.
1 2
def
3
Listing 12.4: xtable.py f i x t a b l e ( filename , rows =
for
4 5 6
[]
line
in
for
8
f . readlines () :
awal = 0 row =
7
widths ) :
f = open ( f i l e n a m e )
9
[]
width field
10
in
widths :
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 )
11
awal = a k h i r
12
r o w s . append ( row )
13
f . close ()
return
14 15 16 17
if
rows
__name__ ==
'__main__ ' :
BAB 12.
import for in print
18
108
OBJECT ORIENTED PROGRAMMING
sys
19
f i l e n a m e = sys . argv [ 1 ]
20
row
21
f i x t a b l e ( filename ,
[3 ,10 ,2 ,8]) :
row
Jalankan.
1
$
2
[ '1 ' ,
' Jeruk ' ,
3
[ '2 ' ,
' Mangga ' ,
' ' ,
4
[ '3 ' ,
' Pisang ' ,
'7 ' ,
python
f i x t a b l e . py
barang . t x t
'34 ' ,
'9000 '] '8000 '] '10000 ']
Perhatikan lagi, hasil masih sama.
Anda bisa uji dengan membuat pega-
wai.txt. Listing 12.5: pegawai.txt 1985 − 08 − 17L
1
1BUMMI DWI PUTERA,
2
2ARIEF SETIADI
3
3CECEP ZAHRUDIN,
4
4NITA PANDRIA
1976 − 09 − 19P
5
5ILHAM
1984 − 11 − 05L
6
6MIRANDA
1978 − 10 − 01P
ST
1972 − 05 − 02L 1972 − 06 − 01L
ST
Lalu xpegawai.py.
1 2 3
Listing 12.6: xpegawai.py
from import
fixtable sys
import
fixtable
4
f i l e n a m e = sys . argv [ 1 ]
5
for
6 7
in print
row
f i x t a b l e ( filename ,
[2 ,22 ,10 ,1]) :
row
Jalankan.
1
$
2
[ '1 ' ,
'BUMMI DWI PUTERA,
3
[ '2 ' ,
' ARIEF SETIADI ' ,
4
[ '3 ' ,
'CECEP ZAHRUDIN,
5
[ '4 ' ,
' NITA PANDRIA' ,
6
[ '5 ' ,
'ILHAM' ,
7
[ '6 ' ,
'MIRANDA' ,
python
f i x p e g a w a i . py
pegawai . t x t '1985 −08 −17 ' ,
ST ' ,
'1972 −05 −02 ' , ST ' ,
'1972 −06 −01 ' ,
'1976 −09 −19 ' ,
'1984 −11 −05 ' ,
'L ' ]
'L ' ] 'L ' ]
'P ' ]
'L ' ]
'1978 −10 −01 ' ,
'P ' ]
Fungsi xtable teruji.
12.3
Tingkat Kerumitan
Kompleksitas masalah kian bertambah.
memperhatikan tipe data
Kini Anda membutuhkan hasil yang
. Perhatikan lagi barang.txt dimana:
BAB 12.
OBJECT ORIENTED PROGRAMMING
kolom pertama, kode barang, bertipe integer
kolom kedua, nama barang, bertipe string
kolom ketiga, stok, bertipe integer
kolom keempat, harga, bertipe integer
109
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
from def
6
types
f i x t a b l e ( filename ,
for
8 9
[]
line
for
11
in
ftypes ) :
f . readlines () :
[]
width ,
ftype
field
=
if not elif
13 14
l i n e [ awal : a k h i r ] . s t r i p ( ) = None
f t y p e == I n t T y p e :
16
field
17
= int ( f i e l d )
row . append ( f i e l d )
18
awal = a k h i r
19
r o w s . append ( row )
20
f . close ()
return
21 22
25 26 27
ftypes :
field :
field
15
24
in
a k h i r = awal + width
12
23
StringType
awal = 0 row =
10
IntType ,
f = open ( f i l e n a m e ) rows =
7
import
Listing 12.7: FixTableType.py
if
rows
__name__ ==
import
'__main__ ' :
sys
f i l e n a m e = sys . argv [ 1 ] fields
=
[
28
[ 3 , IntType ] ,
29
[ 1 0 , StringType ] ,
30
[ 2 , IntType ] ,
31
[ 8 , IntType ]
32
]
BAB 12.
OBJECT ORIENTED PROGRAMMING
for
33 34
in print
row
f i x t a b l e ( filename ,
110
fields ) :
row
Jalankan.
1
$
2
[1 ,
' Jeruk ' ,
3
[2 ,
' Mangga ' ,
None ,
4
[3 ,
' Pisang ' ,
7,
python
F i x T a b l e T y p e . py 34 ,
barang . t x t
9000] 8000]
10000]
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
multi-thread
.
Mengapa kita perlu 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.
Bahkan kalau terkena masalah bandwidth XMLRPC client ini bisa
membutuhkan waktu 60 detik. Bayangkan kalau hanya single-thread. Saat
Thread 1
1
Mengirim SMS 1 selama 10 detik
2
Mengabari status SMS 1 selama 5 detik
3
Mengirim SMS 2 selama 10 detik
4
Mengirim status SMS 2 selama 5 detik
Total waktu yang dibutuhkan untuk mengirim dua SMS adalah 30 detik. Sekarang bandingkan bila menggunakan multi-thread. Saat
Thread 1
Thread 2
1
Mengirim SMS 1 10 detik
2
Mengirim SMS 2 10 detik
Mengabari status SMS 1 5 detik
3
Mengabari status SMS 2 5 detik
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.
1
from
Listing 13.1: testthread.py
threading
import
Thread
111
BAB 13.
2 3 4 5 6 7 8 9
import import from
time os
glob
class def
import
glob
K i r i m ( Thread ) : __init__ ( s e l f ,
k) :
s e l f . k e r j a = True
10
s e l f . kabar = k
11
Thread . __init__ ( s e l f )
12 13 14
def
run ( s e l f ) :
print while if not continue del #s e l f . kabar = s e l f . kabar [ 1 : ] print
t i m e . s t r f t i m e ( '%H:%M:%S ' ) ,
dimulai
15
s e l f . kabar :
18 19
hasil
20
=
s e l f . kabar [ 0 ]
s e l f . kabar [ 0 ]
21 22
'%s KABARI %s ' % ( t i m e . s t r f t i m e ( '%H:%M
:%S ' ) ,
25
sampingan
time . s l e e p ( 1 )
17
24
' Kerja
'
s e l f . kerja :
16
23
112
KERJA SAMPINGAN
def
26
hasil )
join ( self ) :
print
' Kerja
sampingan
berakhir '
s e l f . kerja = False
27
Thread . j o i n ( s e l f )
28 29 30
job_dir =
31
kabar =
' /tmp/ j o b '
[]
32 33
sampingan = Kirim ( kabar )
34
sampingan . s t a r t ( )
35
print while if not continue if not continue
36
t i m e . s t r f t i m e ( '%H:%M:%S ' ) ,
' Pekerjaan
'
37 38 39 40 41 42 43 44
True :
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 ]
utama
dimulai
BAB 13.
45
f = open ( f i l e n a m e )
46
job = f . read ( )
47
f . close ()
48
o s . remove ( f i l e n a m e )
print
49
113
KERJA SAMPINGAN
'%s KERJAKAN %s ' % ( t i m e . s t r f t i m e ( '%H:%M:%S ' ) ,
job )
50 51 52 53
k a b a r . append ( j o b )
print
' Pekerjaan
utama
berakhir '
sampingan . j o i n ( ) Jalankan.
1
$
2
04:56:54
Pekerjaan
3
04:56:54
Kerja
python
t e s t t h r e a d . py utama
dimulai
sampingan
dimulai
Sampai di sini ia menunggu keberadaan le di direktori /tmp/job.
Buka
konsole lain dan buatlah direktori /tmp/job.
1
$
mkdir
/tmp/ j o b
Lalu buatlah le apa saja.
1
$
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
0 4 : 5 6 : 5 6 KERJAKAN h e l l o
2
04:56:57
KABARI
hello
Pointer Perlu diperhatikan variabel kabar yang bertipe list (baris 30). Ini adalah
abel bersama
vari-
, artinya dapat diolah baik oleh thread 1 maupun thread 2. Di
sini berlaku apa yang disebut pointer yang artinya alokasi memori pada baris 30 dengan baris 10
1
s e l f . kabar = k adalah
1
del
sama
. Perhatikan juga penghapusan antrian pertama di baris 20
s e l f . kabar [ 0 ]
dimana pada program biasa bisa saja Anda membuatnya menjadi
1
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.
1
14.1
Pemasangan
$
a p t −g e t
sudo
install
im−gw
Proses instalasi akan meminta Anda menyesuaikan le /etc/im/gw/cong.py.
File ini perlu diisi dengan otentikasi ke database yang sudah dibuat
tadi.
1
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:
1
$
sudo
dpkg− r e c o n f i g u r e
im−gw
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:
1
$
sudo
/ e t c / i n i t . d / im−gw
restart
im-gw juga otomatis hidup saat komputer dihidupkan. Anda bisa memantau log-nya dengan cara:
1
$
2
2011 − 01 − 18
sudo
tail
−f
/ v a r / l o g / im /gw . l o g
05:12:20 ,883
INFO
Start
result
dir
/ var / s p o o l /
INFO
Start
job
pid
1202
im / r e s u l t /
3
2011 − 01 − 18
05:12:20 ,886
114
at
BAB 14.
4
2011 − 01 − 18 on
115
SMS GATEWAY
05:12:20 ,928
port
INFO
Start
result
xmlrpc
server
9317
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:
1
$
a p t −g e t
sudo
install
im−modem
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
$
ps
ax
2
5627
3
28464
|
grep
modem
? SN
0:06
/ u s r / b i n / python
?
0:01
python
Sl
/ 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
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
$
dmesg
2
[
25.972197] a
3
[
|
grep
ttyS
serial8250 :
ttyS0
at
I /O 0 x 3 f 8
( i r q = 4)
is
1 6 5 5 0A
25.973141]
00:07:
ttyS0
at
I /O 0 x 3 f 8
( i r q = 4)
is
a
1 6 5 5 0A
4
[
42.442528] is
5
[ [ [
a a
0000:05:01.0:
a
at
I /O 0 x d 1 0 0
( i r q = 17)
ttyS5
at
I /O 0 x d 2 0 0
( i r q = 17)
0000:05:02.0:
ttyS6
at
I /O 0 x d 7 0 0
( i r q = 19)
ttyS7
at
I /O 0 x d 8 0 0
( i r q = 19)
1 6 5 5 0A
42.538574] is
ttyS4
1 6 5 5 0A
42.538359] is
7
0000:05:01.0:
1 6 5 5 0A
42.442750] is
6
a
0000:05:02.0:
1 6 5 5 0A
Anda bisa menyebutkan semua serial port yang ada pada le /etc/im/modem/modem.conf:
1
[ device ]
2
;
3
p o r t = USB0 USB1 USB2 USB3 USB4 USB5 USB6 USB7 S0
device S6
di
/ dev / t t y
S7
Lalu restart semua modem:
S4
S5
BAB 14.
1
$
sudo
116
SMS GATEWAY
/ e t c / i n i t . d /modem
restart
Tunggu sekitar 30 detik, lalu jalankan
1
$
2
11211
ps
ax
|
grep
?
modem Rl
10:29
python
/ u s r / b i n /modem / d e v /
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
510012541218911. log
sudo
ls
/ v a r / l o g /modem
Selanjutnya mulailah memantau:
−f
1
$
2
2011 − 01 − 18
01:45:22 ,304
INFO
3
2011 − 01 − 18
01:45:24 ,307
INFO
4
2011 − 01 − 18
01:45:24 ,308
INFO
5
2011 − 01 − 18
01:45:24 ,308
INFO
6
2011 − 01 − 18
01:45:24 ,309
INFO
7
2011 − 01 − 18
01:45:26 ,312
INFO
8
2011 − 01 − 18
01:45:26 ,312
INFO
sudo
tail
−n
/ 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
9
2011 − 01 − 18
01:45:26 ,312
INFO
10
2011 − 01 − 18
01:45:27 ,326
INFO
11
2011 − 01 − 18
01:45:27 ,326
INFO
12
2011 − 01 − 18
01:45:27 ,327
INFO
13
2011 − 01 − 18
01:45:27 ,327
INFO
14
2011 − 01 − 18
01:45:29 ,330
INFO
15
2011 − 01 − 18
01:45:29 ,330
INFO
16
2011 − 01 − 18
01:45:29 ,330
INFO
17
2011 − 01 − 18
01:45:31 ,332
INFO
18
2011 − 01 − 18
01:45:31 ,333
INFO
19
2011 − 01 − 18
01:45:31 ,333
INFO
20
2011 − 01 − 18
01:45:32 ,346
INFO
21
2011 − 01 − 18
01:45:32 ,346
INFO
22
2011 − 01 − 18
01:45:32 ,352
INFO
−> <− <− <− −> <− <− −> <− <− <− −> <− <− −> <− <− −> <− <−
30
AT+CSQ AT+CSQ +CSQ :
20 ,0
OK AT+CLIP=1 AT+CLIP=1 OK AT+CGMM AT+CGMM MULTIBAND
9 0 0E
1800
OK AT+CNMI= 0 , 1 , 1 , 1 , 0 AT+CNMI= 0 , 1 , 1 , 1 , 0 OK AT+CMGF=1 AT+CMGF=1 OK AT+CMGL="ALL" AT+CMGL="ALL" OK
Serving
/ d e v / ttyUSB1
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 /
at
pid
and
/
28464
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.
117
SMS GATEWAY
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.
−U
1
$
2
Password
psql
−h
ilham for
user
localhost
3
psql
4
SSL
5
Type
6
t o t a l i n d o=> SELECT i d ,
(8.4.4) connection " help "
7 8 9 10
totalindo
ilham :
( cipher :
for
DHE−RSA−AES256−SHA,
bits :
256)
help .
id
|
s t a t u s FROM im . a g e n t ;
status
−−−−−−−−−−−−−−−−−+−−−−−−−− 510012541218911 (1
|
0
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
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 )
2
t o t a l i n d o −> SELECT
3
INSERT 0
'+628179140068 ' , ' Hello
world ! ' ;
1
Untuk penerima sesuaikanlah dengan nomor handphone Anda. Status pengiriman bisa Anda lihat di tabel im.selesai.
1
t o t a l i n d o=> SELECT i d ,
2
t o t a l i n d o −> FROM im . s e l e s a i
3
id
|
status
pesan
|
status ,
pengirim ,
penerima ,
ORDER BY 1 DESC LIMIT
pengirim
|
penerima
pesan 1;
|
BAB 14.
4 5
118
SMS GATEWAY
−−−−−−+−−−−−−−−+−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−+−−−−−−−−−−−−−− 1065
|
0
|
510012541218911
|
+628179140068
|
Hello
world !
6
(1
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
t o t a l i n d o=> SELECT i d ,
2
t o t a l i n d o −> FROM im . a n t r i a n ;
3 4 5
id
|
kirim
|
kirim ,
pengirim
pengirim , |
penerima ,
penerima
|
pesan pesan
−−−−−−+−−−−−−−+−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+−−−−−−−−−− 1071
|
f
|
+628179140068
|
510012541218911
|
Diterima
6
(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).
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
Gambar 14.1: Alur IM gateway
119
BAB 14.
1
$
a p t −g e t
sudo
120
SMS GATEWAY
im−ym
install
Kemudian sesuaikan /etc/im/ym/cong.py, seperti contoh berikut:
1
users = { ' inforab ' :
' 1234 ' }
dimana inforab adalah Yahoo! account dan 1234 adalah passwordnya. Setelah disimpan tunggu satu menit dan periksa keberadaannya.
1
$
2
2339
ps
ax
|
g r e p ym
?
Sl
24:44
python
/ u s r / b i n /ym
inforab
Log-nya juga tersedia.
1
$
2
2011 − 02 − 25
sudo
tail
−f
/ v a r / l o g /ym/ i n f o r a b . l o g
14:24:46 ,729
INFO
Startup
{ ' status ' :
0}
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:
1
2011 − 02 − 25
15:10:43 ,885
'2011 − 02 − 25 pengirim ' :
INFO I n b o x
15:10:43+7 ' ,
{ ' tgl_operator ' :
' pesan ' :
' hello
world ' ,
'
' info_rab ' }
dan lihat juga tabel im.antrian.
1
t o t a l i n d o=> SELECT i d ,
kirim ,
jalur ,
pengirim ,
penerima ,
pesan
2
t o t a l i n d o −> FROM im . a n t r i a n ;
3 4
id
5 6
|
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.
1
$
sudo
a p t −g e t
install
im−xmpp
Lalu sesuaikan /etc/im/xmpp/cong.py.
BAB 14.
1
121
SMS GATEWAY
u s e r s = { ' i n f o r a b @ g m a i l . com ' :
2
{ ' password ' :
3
' port ' :
4
' 1234 ' ,
5223 ,
' sasl ' :
' server ' :
True ,
' t a l k . g o o g l e . com ' }
} Sesuaikan inforab dan 1234.
Setelah disimpan tunggu satu menit hingga
daemon-nya up. Setelah hidup, Anda bisa lakukan pengujian yang serupa seperti ym. Jika Anda ingin menggunakan server lokal, bisa coba daftar ke jabber.rab.co.id menggunakan Pidgin.
Lalu sesuaikan /etc/im/xmpp/cong.py seperti contoh
berikut.
1
u s e r s = { ' i n f o r a b @ g m a i l . com ' :
2
{ ' password ' :
3
' port ' :
4
' 1234 ' ,
5223 ,
' sasl ' :
' server ' :
' i n f o @ j a b b e r . rab . co . i d ' :
5
True ,
' t a l k . g o o g l e . com ' } , ' 1234 '
} Ya, im-xmpp juga dapat menghidupkan lebih dari satu account. Kebetulan jabber.rab.co.id menggunakan kongurasi yang lebih sederhana sebagaimana contoh di atas.
14.4
Broadcast
Anda telah memiliki banyak pelanggan dan ingin mengirim pesan yang sama ke mereka. Dengan mudah lakukan query berikut.
1
INSERT INTO im . a n t r i a n ( p e n e r i m a ,
2
SELECT no_hp ,
' Selamat
pesan )
t r a n s a k s i ' FROM p e l a n g g a n ;
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.
1
$
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.
1
t o t a l i n d o=> SELECT
2
nextval
3
−−−−−−−−−
4 5
122
SMS GATEWAY
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
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 )
2
t o t a l i n d o −> SELECT 8 ,
' Uji
broadcast ' ,
' Selamat
transaksi
';
3
INSERT 0
1
Field judul hanya untuk keterangan saja. Field pesan-lah yang nanti akan dikirim. Berikutnya tambahkan penerima pesan.
1
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 ,
2
t o t a l i n d o −> SELECT 8 ,
3
INSERT 0
penerima ) '+628179140068 ';
1
Jalur default yang digunakan adalah 1 (modem). Untuk jalur lainnya sertakan eld jalur.
1
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
2
t o t a l i n d o −> SELECT
3
INSERT 0
, jalur ) 8 , ' info_rab ' , 5 ;
1
Dimana jalur 5 adalah ym.
Indeks __name__, 34
integer, 10
ALTER TABLE, 49, 50
KDE, 5, 6
Android, 4
konsole, 5
apt-get install, 5 apt-get update, 5
length(), 55
autoincrement eld, 45
Linux, 4 locale-gen, 37
BlankOn, 5
localtime(), 27
break, 13
lower(), 8 lpad(), 55
continue, 14 CREATE TABLE, 43
Mac, 4
CREATE VIEW, 51
mktime(), 28
createdb, 42
modul datetime, 29
createuser, 41
modul locale, 36 modul time, 27
Data Denition Language, 50 Data Manipulation Language, 50
nextval(), 46
date(), 29
None, 38
datestyle, 47
NOT NULL, 43
datetime(), 29 DEFAULT, 43
objek hampa, 38
DELETE FROM, 49
ORDER BY, 47
DROP TABLE, 50
ORDER BY, DESC, 48
dropdb, 42 dropuser, 41
pg_dump, 53 pg_restore, 53
epoch, 28
plpgsql, 5
eval(), 19
plpython, 5 postgresql.conf, 47
oat, 10
PRIMARY KEY, 43
for, 15
print, 8, 19
formatting, 16, 18
psql, 5, 42
fullpath, 53 rata kanan, 54 Gnome, 6
raw_input(), 9, 26
int(), 18, 26
sequence, 45 123
INDEKS
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
124