วันศุกร์ที่ 23 ตุลาคม พ.ศ. 2552

Python send mail email e-mail

วันนี้ได้ไปเล่นกับ blog ของ นายบอส มามีในส่วนการส่งเมล์ เผอิญที่เป็นเป็น รูปแบบธรรมดา
ยาว ๆ เลยขอมาจับแยกให้ดูดีขึ้น แต่ยังไม่ได้ test เลย คือเอามาแปลงอย่างเดียว
เดี๋ยวถ้าว่าง ๆ จะมาประกอบและ test ใหม่

ตอนนี้ง่วงมาก ๆ เลย Z z z z . . .

อ้างอิงจาก : http://bluegear.wordpress.com/2009/10/20/email-attach-file-with-python/#comment-32

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import xml.etree.ElementTree as ET

class EMail(Object):
    def init(self, url_mail_server):
        self.url_mail_server = 'mailserver.company.com'
        self.smtp = smtplib.SMTP(self.url_mail_server)
        
    def gen_template(self, **kw):
        root = ET.Element("html")
        head = ET.SubElement(root, "head")
        body = ET.SubElement(root, "body")
        p = ET.SubElement(body, "p")
        a = ET.Element('a', href='http://www.companyhomepage.com')
        img = ET.Element('img', src='cid:image1')
        a.append(img)
        p.append(a)
        return ET.tostring(root, 'UTF-8')
        
    def msg(self, **kw):
        msg = MIMEMultipart('alternative')
        msg['Subject'] = kw.get('subject')
        msg['From'] = kw.get('sender')
        msg['To'] = ''.join(['%s, '%b for b in kw.get('receiver')]).rstrip(', ')
        msg.attach(MIMEText(gen_template(**kw), 'html'))
        
        fp = open(kw.get('image'), 'rb')
        img = MIMEImage(fp.read())
        fp.close()
        img.add_header('Content-ID', '')
        msg.attach(img)
        return msg
        
    def send(self, **kw):
        self.smtp.sendmail(kw.get('sender'), kw.get('receiver'), msg(**kw))
        
    def finish(self):
        self.smtp.quit()
        

if '_main_' == _name_:
    d = {}
    d['sender'] = 'sender@company.com'
    d['receiver'] = ['vip1@something.com', 'vip2@something.com']
    d['subject'] = 'Test Embed Image With Url'
    d['image'] = 'image1.gif'
    
    email = EMail('mailserver.company.com')
    email.send(**d)
    
    d['image'] = 'image2.gif'
    email.send(**d)
    
    d['image'] = 'image3.gif'
    email.send(**d)
    email.finish()

วันพุธที่ 21 ตุลาคม พ.ศ. 2552

Python lambda

lambda ในภาษา python นับเป็นเครื่องอำนวยความสะอาดอย่างหนึ่ง
คือ ทำให้เราสามารถ ย่น code ที่ไม่มีอะไรซับซ้อน ได้ ทำให้
การอ่าน code เป็นไปอย่างสบายตา แต่ไม่ใช่ว่าต้องใช้ทุกครั้งไปนะครับ
ดูที่ความเหมาะสม

จริง ๆ แล้วก็เป็นเหมือนการเขียน function ที่ไม่ซับซ้อนให้อยู่
ในบรรทัดเดียว

ตัวอย่าง ถ้าเราเขียน function ขึ้นมาอย่างหนึ่ง
ใช้หาเลขคู่ ก็จะได้ประมาณนี้
a = [1, 2, 3, 4]
ab = []
for b in a:
    if (b % 2) == 0:
        ab.append(b)
ยาวไป เอาใหม่
a = [1, 2, 3, 4]
ab = [b for b in a if (b % 2) == 0]

จริง ๆ แค่นี้ก็ดูดีมีชาติตระกูลแล้ว
แต่ลองเขียนใหม่ดู

a = [1, 2, 3, 4]
ab = filter(lambda b: (b % 2) == 0, a)

lambda ในที่นี้จะรับเอาค่า ทีละค่าจาก array a มา check
คือนำเอามาแทน b ใน lambda แล้วเช็คว่า หาร 2 แล้ว
ลงตัวใหม ถ้าไม่ลงก็จะ return False ถ้าพอดี ก็ return True

ในที่นี้ใช้ filter มาช่วย จัดการกับค่า array a
ถ้าเป็น True จะ return ค่าออกมา ถ้าเป็น False ก็ไม่เอาค่านั้น

------------------------------

ลองมาดูง่ายกว่านั้น
เอาเป็นว่า มีข้อมูล อยู่
a = [1, 2, 3, -3, 4, 'a']

ต้องการดึงค่าออกมาเฉพาะ เลขจำนวนเต็ม มากกว่าศูนย์ และเป็นเลขคู่ แล้วเอาไป *2

อันนี้แบบย่อสุด ๆ

a = [1, 2, 3, -3, 4, 'a']
ab = [b*2 for b in a if int == type(b) and b > 0 and (b % 2) == 0]

เริ่มอ่านยากแระ ลองแยกในส่วน check ออกเป็น function ดู

a = [1, 2, 3, -3, 4, 'a']
def find_x(x):
    return int == type(x) and x > 0 and (x % 2) == 0

ab = [b*2 for b in a if find_x(b)]

ลองเขียนแยกเพื่อให้ง่ายต่อการแก้ไข
และเขียนเป็น แต่ละ function

a = [1, 2, 3, -3, 4, 'a']
def is_type_int(x):
    return int == type(x)

def is_over_zero(x):
    return x > 0

def is_mod_two(x):
    return (x % 2) == 0

def cal_double(x):
    return x*2

def chk_x(find_x, cal_x, x):
    ab = []
    for i in x:
        ff = True
        for f in find_x:
            if not f(i):
                ff = False
                break
        if ff
            ab.append(cal_x(i))
    return ab

ab = chk_x([is_type_int, is_over_zero, is_mod_two], cal_double, a)

มองว่า function ย่อย ไม่ได้สำคัญอะไร ก็ยุบไปซะ

a = [1, 2, 3, -3, 4, 'a']
def chk_x(find_x, cal_x, x):
    ab = []
    for i in x:
        ff = True
        for f in find_x:
            if not f(i):
                ff = False
                break
        if ff:
            ab.append(cal_x(i))
    return ab

ab = chk_x([lambda i: int == type(i), lambda i: i > 0, lambda i: (i % 2) == 0], lambda i: i*2, a)

คือคิดเผื่อว่า เอาเป็น หาร 3 ลงตัว แล้ว เอาไป *3 ก็จะแก้แค่นี้อะครับ

ab = chk_x([lambda i: int == type(i), lambda i: i > 0, lambda i: (i % 3) == 0], lambda i: i*3, a)

ยิ่งเขียนยิ่งไปกันใหญ่ เป็นตัวอย่างเฉย ๆ ครับ สำหรับการประยุกต์ใช้

วันพฤหัสบดีที่ 10 กันยายน พ.ศ. 2552

run turbogears2 on eclipse

และแล้วก็ต้องหันหน้ามาใช้ eclipse อีกเช่นเคย หลังจากห่ายหายจากการเขียน Java
ไปสักพัก คราวนี้เกิดอะไรขึ้นทำไมต้องใช้ eclipse
ก็เนื่องจาก javascript นี่สิที่เป็นปัญหา ต้องยอมรับว่า tool ฟรี หาได้บน eclipse
จริง ๆ ใช้ JSLint ในการ validate javascript syntax

แล้ว project ที่ใช้ก็คือ Turbogears2 เอาไงดี ใหน ๆ ก็พี่งพา tool ระดับเทพ
นี้แล้วก็ต้องใช้ให้มันคุ้มกับความหน่วง เลยไปเจอวิธีมา ว่าจะ run project ที่เป็น
Turbogear ได้อย่างไร ก็ไปเจอวิธีมา ตามนี้ครับ

สิ่งที่ต้องทำแล้ว
1. install Python 2.5, 2.6
2. install Turbogears 2.0
3. install Eclipse 3.4+

ส่วนแรก เป็นการ
install PyDev(tool ที่ทำให้ eclipse รู้จักภาษา python)

1) ที่ menu bar เลือก Help >> Install New Software >> Add...
2) set Name : Pydev
3) set Location : http://pydev.org/updates/ and click button ok
4) check box PyDev only and click button Next>
5) download and install by click button Finish
6) restart eclipse after add plugin


setup PyDev(ชี้ไปยัง paht python ที่ทำการ install)

1) ที่ menu bar เลือก Window >> Preference
2) ที่ menu ด้านซ้ายมือ เลือก Pydev >> Interpreter - Python
3) ด้านขวามือจะขึ้น Python Interpreters ให้ Click New... ให้เลือก path python
    << path install >>\tg2env\Scripts\python.exe
    ในส่วนนี้จะเลือก python จาก turbogear environment เป็นหลัก เพราะ tool บางตัว เช่น cx_Oracle ของ windows บังคับให้ลงที่เดียว และต้องไว้ใน env ของ tg2 ด้วยครับ เคยกล่าวไว้ใน post เก่าลองไปดูกันได้ แล้วจึง Click New... ให้เลือก path python
    C:\Python25\python.exe
    เป็นรองครับ ลงมาเพราะต้องใช้ tool บางอย่างบน env python


ขั้นตอนต่อไปคือ
สร้าง project ด้วย tg2 หรือเป็น project ที่มีอยู่เดิมแล้ว
ให้ใช้คำสั่ง (ถ้าเคยทำแล้วอาจไม่ต้องทำอีกครับ)
ทำการ set env turbogear โดนเข้าไปที่

cd << << path install >>\tg2env\Scripts\
activate

เข้าไปที่ path project จะเจอ file setup.py

python setup.py install

หรือ ถ้า error ฟ้องให้ใช้ develop ก็เปลี่ยนเป็น

python setup.py develop

เสมอ เพื่อให้ project มีการ load resource และจัดเตรียมอะไรบางอย่างให้พร้อมครับ

ในส่วนที่สอง เป็นการ set run-script turbogear2 on eclipse

For TG2, provided pydev is setup.

1) Under Run->External Tool->External Tools Configurations
2) Create a "New Launch Configuration"
3) On the Main Tab
      Name: TG2 App Start
      Location: /usr/local/devenv/tg2env/bin/paster (Your Path to
paster)
      Working Directory: ${project_loc}
      Arguments: serve --reload development.ini
4) On the Common Tab
      Check "Display in favorites menu" > External Tools
5) Apply and close
6) In Navigator Window, highlight the project folder.
7) Under Run->External Tool->External You can select the new "TG2 App
Start"


ถ้าไม่ติดปัญหาอะไร ที่ console ของ eclipse จะขึ้น log เวลาที่เราเข้า
localhost:8080 << ตัวอย่าง url ที่ set ไว้สำหรับใช้งน

Basic Ext JS

เป็น Web Template ที่ผสานระหว่าง Html(CSS + DOM) + Javascript

โดน site หลักอยู่ตามนี้ครับ
Ext JS

ข้อดีคือ
สามารถ แยกในส่วนที่เป็น server site และ client site ได้ขาดจากกันจริง ๆ
และให้ client site ทำการติดต่อกับ server site ได้โดย Ajax ที่รองรับทั้ง
JSON และ XML ทำให้ Web ดูรายรืนและเนียรจริง ๆ

ตามนี้ไปก่อนนะครับ เดี๋ยวมาแกะใส่ทีละอันครับ
Tutorial:Ext StartUp Guide (Thai)
Tutorial:Introduction to Ext 2.0 (Thai)

วันพุธที่ 9 กันยายน พ.ศ. 2552

Basic PL/SQL

จนแล้วจนรอด programmer ก็ต้องเขียน SQL อันนี้ง่ายถูไถ ไปเรื่อยก็รอด
แต่พอมาเป็น Database ของ Oracle แล้วนี้
มันปวดใจจริง ๆ กับ PL/SQL ทำไมต้องมาเขียน program สองที่หว่า
แต่เอาเป็นว่า เอ่อ เขียนก็เขียนเพราะงานมันก็คนละแบบจริง ๆ ครับ

โครงสร้างภาษา PL/SQL

[ DECLARE << ประกาศตัวแปร ถ้าไม่มีไม่ต้องใส่ก็ได้

]
BEGIN << เริ่ม process
[ EXCEPTION << ประกาศการจัดการกับ Error ถ้าไม่มีไม่ต้องใส่ก็ได้

]
END; << จบ process ต้องมี ; ปิด้านเสมอ


การเขียน ซ้อนกันทำได้โดย

DECLARE << เริ่ม process หลัก
BEGIN
  DECLARE << เริ่ม process ย่อย
  BEGIN
  END << จบ process ย่อย
END << จบ process หลัก


comment code
1. บรรทัดเดียว ใช้

-- comment

2. หลายบรรทัด ใช้

/*
comment
comment
*/


รูปแบบการประกาศตัวแปร

variable_name [CONSTANT] datatype [NOT NULL][{DEFAULT|:=}initial_value];


variable_name << คือชื่อตัวแปร
CONSTANT << เป็น keyword ว่าตัวแปรนี้ เป็น constant variable (ค่าคงที่)
ไม่สามารถเปลี่ยนค่าได้
datatype << คือประเภทของตัวแปร เช่น
NUMBER[(p[,s])] number (p = precision, s = scale)
CHAR[(n)] fixed length character (default = 1 char)
VARCHAR2(n) variable length character n คือ maximum length
BOOLEAN logical มี 3 ค่า คือ (True, False, Null)

NOT NULL << เป็นการตั้งกฎไว้ว่า ตัวแปรนี้ต้องมีค่าเสมอ (ห้ามเป็น NULL)
{DEFAULT|:=}initial_value << เป็นการกำหนดค่าเริ่มต้นให้ตัวแปร

หมายเหตุ ถ้าระบุ NOT NULL หรือ CONSTANT keyword ในการประกาศตัวแปรแสดงว่า
ต้องมีการกำหนด Initial value ให้ตัวแปรนั้นด้วย

คำสั่งภาษา SQL ได้ดังนี้
Data Retrieval

SELECT

Data Manipulation Language

INSERT
UPDATE
DELETE

Transaction Control

COMMIT
ROLLBACK
SAVEPOINT


การ Assign ค่าตัวแปร

ชื่อตัวแปร := expression ;


การทำงานตามเงื่อนไขด้วย IF statement

IF condition THEN
statements ;
[ ELSIF condition THEN
statements;
]
[ ELSE
statements;
]
END IF;


การทำงานซ้ำ LOOP

LOOP
statement 1;
statement 2;
...
EXIT [ WHEN condition ];
END LOOP;


การทำงานซ้ำ FOR

FOR index IN [REVERSE]
lower_bound..upper_bound
LOOP
statement 1;
statement 2;
END LOOP;


[REVERSE] << ใช้สำหรับวน loop แบบย้อนหลัง (จาก upper_bound ลดลงทีละ 1 จนถึง lower_bound)
lower_bound..upper_bound << ตัวอย่างเช่น 1..10

การทำงานซ้ำ WHILE

WHILE condition
LOOP
statement 1;
statement 2;
END LOOP;


การนำ PL/SQL ไปใช้งาน

1. เขียนในรูปแบบของ Anonymous Block (pl/sql block)

[ DECLARE variable_declaration ; ]
BEGIN
executable - code ;
END;


2. ใช้พัฒนาเป็น SubProgram สำหรับเรียกใช้ได้มี 3 ลักษณะ
2.1 PROCEDURE
เป็นโปรแกรมย่อยที่ทำงานอะไรบางอย่าง โดยสามารถรับ parameter มาทำงานได้

PROCEDURE name [ ( parameter,….) ]
IS

pl/sql block;

2.2 FUNCTION
เป็นโปรแกรมย่อยที่นิยมใช้ เพื่อหาค่าอะไรบางอย่าง แล้วคืน กลับมาเป็นชื่อของตัว function เอง

FUNCTION name [ ( parameter,…) ]
RETURN datatype
IS

pl/sql block;

2.3 PACKAGE เป็นการรวบรวม PROCEDURE หรือ FUNCTION หลาย ๆ ตัว
ถ้าไว้ด้วยกัน เพื่อง่ายต่อการควบคุมในแง่ privilege และเป็นหมวดหมู่ดีขึ้น

วิธีการประกาศ parameters ใน Subprograms

parameter.name [IN | OUT | IN OUT] datatype [{ := | DEFAULT} expr];

parameter.name << ชื่อ parameter
mode
IN = รับค่าเข้า
OUT = ส่งค่ากลับ
IN OUT = รับค่าเข้าและส่งค่ากลับ

datatype ชนิดของข้อมูล ไม่ต้องระบุความยาว
[{ := | DEFAULT} expr] ใช้ initial ค่า
กรณีเป็น parameter ใน mode IN และเวลาเรียกไม่ระบุค่า parameter เข้ามา

วันจันทร์ที่ 31 สิงหาคม พ.ศ. 2552

install django on windows

ในที่สุดก็ลองมาจับ django (web framework ทางฝั่งพี่ python)
หลังจากที่จับ turbogear อยู่นานประมาณนึงครับ ใจริงก็ยังเป็นสาวก Turbogear อยู่น้าาาา
วันนี้ก็ลองเล่น ๆ ดู ครับ จะพยายาม
จริง ๆ ก็ recommend ให้ลงบน linux เพราะมันทำมาเพื่อสิ่งนั้น
แต่ developer ไทยน้อยคนคงใช้ linux บนเครื่องส่วนตัว ที่ทำงาน เนื่องจาก มันเอาไว้เล่นเกมส์ไม่ได้ !!!
ไม่เกี่ยว คือมันเปิด และจัดการงานเอกสาร๖(เช่น Word, Excell)ไม่สวย ถึง ห่วยแตก
office เลยยังต้องยกให้ windows เป็นผู้ชนะ คร๊าาาาาาบ
อีกอย่างคงกิน resource น่าดูครับ ถ้าต้องใช้ vmware ในการสร้าง linux บน windows และการทำงาน
แบบสลับไปมา ให้ยุ่งยาก
เอ่า นอกเรื่องมาพอสมควร เริ่มเลยดีฟ่า

ขั้นแรก ก็ทำการลง python ในกรณี windows นั้น
ไปเอามาจากที่นี่นะครับ แนะนำว่า ต้องลงท้ายว่า
Windows installer << สำหรับ เครื่องทั่วๆ ไป
Windows AMD64 installer << สำหรับพวก 'บ้าพลัง' เครื่องแรงที่ลง os เป็น 64 ครับ
โดยเช็คจาก คลิ๊กขวาที่
My Computer(สำหรับ windowsXP) หรือ
Computer(สำหรับ windows7)
แล้วเลือก Properties แล้วก็พยายามดูนะ
version Python ที่แนะนำ ควรเป็น 2.5 - 2.6 << ยังไม่แนะนำ 3.0 นะครับ
Python
ส่วนใหญ่จะเป็นแกมบังคับให้ลงที่

C:\Python25 << อันนี้สมมติเป็น v 2.5 ครับ

ไม่ควรแสดงความฉลาดโดยการไปเลี่ยน Path ให้มันเองครับ
เพราะ tool ต่าง ๆที่จะลงในส่วนใหญ่จะบังคับ path นี้อยู่แล้ว เดี๋ยวจะหาว่า พี่ไม่เตือนนะน้อง

ต่อจากนั้นก็ลงอย่างอื่นก่อน(ทำไมยังไม่ได้ลง django ซะที) ส่วนใหญ่เค้าพูดกันว่าให้ลงก่อน ผมก็ไม่เข้าใจ?
เป็น driver database ครับ เลือกตามความประสงค์ครับ ตามนี้
1.PostgreSQL << ฟรี ๆ outsource หน้าชื่นตาบาน แต่ user รวยๆ บอกผมเลือก Oracle เซ็งเป็ด!!!
จะลง psycopg

2.MySQL << ฟรีสำหรับทดลอง แต่ถ้าเอามาทำเงินต้องเสียตัง แต่ง่ายต่อการเรียนรู้ ถ้าจะเริ่มครับ
จะลง MySQLdb

3.SQLite << ไม่รู้ว่าเคยได้ยินกันเปล่าครับ ก็ฟรีอะ แต่ไม่รู้รายละเอียดเท่าไร ท่าทางไม่ค่อยแข็งแรงเท่าสองตัวบน
จะลง pysqlite

4.Oracle << ยอมรับกันทั่วโลกครับ เพราะพี่ท่านทุนหนาไปซื้องานที่เค้าจะเกิด และจะดังเอามาเป่าดับเล่น
user ค่อยข้างเชื่อใจ จริง ๆ feature ค่อยข้างมากมาย ถ้าซื้อมาแล้วเอาไว้เก็บข้อมูลอย่างเดียว บอกได้เลยว่า
เสียดายตังมากมายครับ แต่เอาเถอะนะ คนจ่ายเค้าจะเอา
จะลง cx_Oracle

*driver database ที่กล่าวมาข้างต้นนี้ ถ้า link เสียก็ใช้ชื่อไป search เอาเองนะครับ แต่ถ้าจะกรุณาก็แจ้ง
ให้กระผมทราบหน่อยก็ดีครับเผื่อจะได้เป็นประโยชน์ในการ update ในภายภาคหน้า
**อย่าลืมนะครับ เวลา download มาเลือกเป็น version ของ windows ครับ ไม่งั้นมันจะไม่ไปลงใน

C:\Python25\Lib\site-packages

เป็น path สำหรับ plugin สำหรับ Python ก็ว่าได้ ส่วนใหญ่เวลาลงแล้วจะไปรวมกันในนี้ครับ
ไม่เชื่อเข้าไปดูนะครับ

ถึงเวลาพระเอกแล้ววววววว
ลง django
เข้าไปที่ web django แล้วก็
load มาเลยครับ file นี้ Django-1.1.tar.gz
เสร็จแล้วก็นำมาแยกด้วย winrar หรือ อะไรก็ได้ที่มันจะคลาย zip file นี้ออกมาได้ครับ
ก็จะได้ folder

D:\python\Django-1.1

ตอนนี้ผมเอาไว้ drive D:\python ครับ อันนี้สร้างเอง จะเอาไว้ที่ใหนก็ได้นะครับ
หลังจากนั้นก็เข้าไปยัง path ดังกล่าวด้วย command prompt แล้วก็พิมพ์คำสั่ง

D:\python\Django-1.1>python setup.py install

ก็จะมีข้อความขึ้นตามนี้

running install
running build
running build_py
running build_scripts
running install_lib
running install_scripts
running install_data
running install_egg_info
Removing C:\Python25\Lib\site-packages\Django-1.1-py2.5.egg-info
Writing C:\Python25\Lib\site-packages\Django-1.1-py2.5.egg-info


ในที่นี้ไม่แน่ใจว่ามันมีอะไรเปลี่ยนแปลงหรืออะไรจำเป็นหรือเปล่าครับ ไว้ทราบเรื่องจะแจ้งให้อีกที

ต่อมาก็เริ่ม start new project กันดีกว่า(นี่แหละเวลาแห่งการรอคอยยยยย)
สมติ path ขึ้นมา จะใช้เป็นอะไรก็ได้นะครับ

D:\python\project


ถ้าเป็น linux เมื่อลงเสร็จจะใช้แค่

django-admin.py startproject testproject


แต่ผมพยามยามแระ ทำแล้วมันดันไปเรียก popup ของ python shellscript ขึ้ันมา เวรกรำแล้วสิ
บังเอิญมาเจอ blog ที่นึงเค้าเขียนไว้ เต็ม path ลองมาลองดูก็ได้เป็นตามด้านลางนี้ครับ
ในที่นี้ให้ชื่อ project ว่า testproject

D:\python\project>D:\python\Django-1.1\django\bin\django-admin.py startproject testproject
D:\python\project>

... เงียบ ... เหมือนไม่มีอะไรเกิดขึ้น
แต่ลอง dir ดู
ว้าวววว ออกมาและครับ
D:\python\project>dir
Volume in drive D is Project
Volume Serial Number is 664E-B00A

Directory of D:\python\project

31-08-09 03:05 <DIR> .
31-08-09 03:05 <DIR> ..
31-08-09 03:05 <DIR> testproject


ลองไปดูข้าในกันครับว่ามีอะไรบ้าง

D:\python\project\testproject>dir
Volume in drive D is Project
Volume Serial Number is 664E-B00A

Directory of D:\python\project\testproject

31-08-09 03:05 <DIR> .
31-08-09 03:05 <DIR> ..
31-08-09 03:05 557 manage.py
31-08-09 03:05 2,862 settings.py
31-08-09 03:05 569 urls.py
31-08-09 03:05 0 __init__.py


มีแค่นี้ !!! เทียบกับ turbogear แล้ว ผมทำอะไรผิดเปล่าหว่า ทำไมน้อยจัง
เอาเป็นว่า ทำใจดีเชื่อใจตัวเอง
ไป start เลยดีกว่า เพราะยังไม่รู้จะเขียนอะไรดี

D:\python\project\testproject>python manage.py runserver
Validating models...
0 errors found

Django version 1.1, using settings 'testproject.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
[31/Aug/2009 03:12:42] "GET / HTTP/1.1" 200 2063


ไม่ติดปัญหาอย่างที่คิด โดยการเข้า browser
http://localhost:8000/


แล้วบนหน้า web จะมีคำว่า
It worked!
Congratulations on your first Django-powered page.

ขึ้นต้นครับ

เวลาจะ stop ก็ใช้ Ctrl + Break
แต่ผมก็เผลือใช้ Ctrl + c ครับ เพราะ ชินมือครับ ไม่แน่ใจว่าต่างเปล่า
เพราะเวลาผมใช้ Ctrl + Break มันก็ขึ้นที่ command prompt ว่า
^C
เลยเอาเป็นว่าแบบคิดไปเองนะ ว่าผ่าน!!!

อ่า เสร็จไปหนึ่ง แค่ลงก็เหงื่อท่วมแระ กว่าจะรู้ว่า create new project บน windows อย่างไง
เกือบจะหลวมตัวไปใช้
How to use Django with Apache and mod_wsgi
ถึงกับงานเข้าเลย เพราะ ต้องอธิบายอีกสองตัว

จบในส่วน install django กับ create new project ไว้แค่นี้ก่อน
เรื่องจะเขียนอะไรคงเป็นบทหน้าแระครับ

วันพุธที่ 26 สิงหาคม พ.ศ. 2552

java batch list model

ตัว java class model ตัวนี้ เขียนขึ้น เนื่องจากเกิดปัญหา การเก็บข้อมูลเป็น list โดยแต่ละ
list มีหัวข้อคนละชุด งงหละสิ
ถ้ามองก็คล้าย database ครับ(จริง ๆ ก็ทำเพื่อสิ่งนั้น)
คือ ผมมีข้อมูล
A ประกอบไปด้วย A1, A2, A3, - A10
B ประกอบไปด้วย Ba, Bb, Bc, Bd, - Bz
C ประกอบไปด้วย Ca2, Ca4, Ca8, Cb2, Cb4, Cb8
คร่าว ๆ ประมาณนี้

แล้วถ้าผมอยู่ ๆ ต้องการเพิ่ม
D ประกอบไปด้วย ก, ข, ค, ง, - ฮ

แล้วต้องการลบ A กับ B ออกจากระบบ

แล้วต้องการดูว่า ตอนนี้เหลืออะไรอยู่บ้าง เหลืออยู่เท่าไร

แล้วสมมติว่าอนาตค
อาจต้องการ เพิ่มหรือลด บางตัวแปร
อาจต้องการ เพิ่มหรือลด ข้อมูลในบางแปร

จากข้างบนนี้ ปัญหาคือ แล้วผมจะเอาอะไรไปเก็บข้อมูลที่ระบุอะไรไม่ได้ นี้
ไม่รู้อะไรจะมาอะไรจะไป จะทำอย่างไงให้ยืดหยุ่น
กรณี list ข้อมูลไม่ใช่แค่ text

รายละเอียด
...(เดี๋ยวว่างมาเขียนต่อนะ ดู ๆ code กันไปก่อน)

ตัวนี้ คุณ Notto เป็นคนช่วยแปลงให้เข้ากับ java v.1.4 ครับ เนื่องจาก ตอนแรกผมจะใส่
type Generic เป็น Object กับ String

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class BatchListModel {

    private Set idSet;
    private Map batchDataMap;

    public BatchListModel() {
        idSet = new HashSet();
        batchDataMap = new HashMap();
    }

    public void putAllStringData(String id, List list) {
        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
            String item = (String) iterator.next();
            putStringData(id, item);
        }
    }

    public void putAllObjectData(String id, List list) {
        for (Iterator iterator = list.iterator(); iterator.hasNext();) {
            Object item = (Object) iterator.next();
            putObjectData(id, item);
        }
    }

    public void putStringData(String id, String obj) {
        idSet.add(id);
        List objList = (List) batchDataMap.get(id);
        if (null == objList) {
            objList = new LinkedList();
        }
        objList.add(obj);
        batchDataMap.put(id, objList);
    }

    public void putObjectData(String id, Object obj) {
        idSet.add(id);
        List objList = (List) batchDataMap.get(id);
        if (null == objList) {
            objList = new LinkedList();
        }
        objList.add(obj);
        batchDataMap.put(id, objList);
    }

    public List getData(String id) {
        List objList = (List) batchDataMap.get(id);
        return objList;
    }

    public int getSize(String id) {
        int size = 0;
        try {
            List objList = (List) batchDataMap.get(id);
            if (null != objList) {
                size = objList.size();
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        return size;
    }

    public Set getId() {
        return idSet;
    }

    public Set getId(String id) {
        Set newStrSet = new HashSet();
        for (Iterator iterator = getId().iterator(); iterator.hasNext();) {
            String str = (String) iterator.next();
            if (str.equalsIgnoreCase(id)) {
                newStrSet.add(str);
            }
        }
        return newStrSet;
    }

    public int getIdSize(String id) {
        int size = 0;
        try {
            Set objList = getId(id);
            if (null != objList) {
                size = objList.size();
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        return size;
    }

    public void removeDataById(String id) {
        batchDataMap.remove(id);
    }

    public void clear() {
        this.clearId();
        this.clearData();
    }

    public void clearId() {
        idSet.clear();
    }

    public void clearData() {
        batchDataMap.clear();
    }
}

วันอังคารที่ 25 สิงหาคม พ.ศ. 2552

Java for loop list & map ,Python for loop array & map

ในส่วนของการเขียน program นั้น ย่อมต้องมีเรื่องการอ่าน data มากมายเข้ามาเกี่ยวข้องเป็นธรรมดา
ถ้าเป็นเรื่อง List การวนดึงข้อมูลก็คงไม่ใช่เรื่องยุ่งยาก แต่สำหรับ ดังตัวอย่าง
List<String> strList = new LinkedList<String>();
strList.add("comment1-hi,");
strList.add("comment2-i");
strList.add("comment3-am");
strList.add("comment4-Programmer");

for(String str : strList){
    System.out.println(" >> " + str):
}
แต่ในกรณีข้อมูล ที่เป็น Map<String, String> งงว่าทำไมไม่ทำ for ได้ แต่ก็่ช่างครับ
เราย่อมมีวิธีตามนี้เลยครับ
Map<String, String> strMap = new HashMap<String, String>();
strMap.put("comment1", "hi,");
strMap.put("comment2", "i");
strMap.put("comment3", "am");
strMap.put("comment4", "Programmer");
แบบ โบราณ ทำไว้เผื่อจะเอา keys ไปทำอย่างอื่น -.-" คิดเผื่อ เหอ ๆ
Set<String> keys = strMap.keySet();
for( String key : keys ) {
    System.out.println( " >> " + key + "-" + strMap.get( key ) );
}
แบบใหม่ใช้แล้วทิ้ง ^_^
for( Map.Entry<String, String> entry : strMap.entrySet() ) {
    System.out.println( " >> " + entry.getKey() + "-" + entry.getValue() );
}
ลองไปดู python เล่น ๆ ซิ (python จะไม่มี map นะครับ เขาใช้ dict คล้ายกัน)
strDict = {}
strDict['comment1'] = 'hi,'
strDict['comment2'] = 'i,'
strDict['comment3'] = 'am'
strDict['comment4'] = 'Programmer'
เวลาดีงข้อมูลออกมาก็
for k in strDict:
    print ' >> %s-%s' %(k, strDict[k])
!อ่าว เสร็จแล้วหรอ
งั้นไปดู list ของ python กันบ้าง จริง ๆ ก็คือ array แบบ java ครับ
strList = []
strList.append("comment1-hi,");
strList.append("comment2-i");
strList.append("comment3-am");
strList.append("comment4-Programmer");
เอ้าาา วนซิ
for l in strList:
    print ' >>', l
ถ้าลองเพิ่มว่า จะ print เฉพาะ comment ที่ลงท้ายเลขคู่ java จะยาวใหมน้อ ....
เมื่อรู้อย่างนี้แล้ว ก็เลิกเขียนจาวา แล้วไปเขียน python กันดีกว่า อิอิ

วันศุกร์ที่ 7 สิงหาคม พ.ศ. 2552

ubuntu auto script startup

หลายคนที่เกี่ยวข้องกับ server นั้น จะเจอปัญหาที่ว่า เวลาที่เครื่อง server down ไปเนื่องจากสาเหตุอะไรก็แล้วแต่
คนดูแลนั้น ก็จะต้องมานั่ง start service ทีละตัวๆ แต่ถ้าวันนั้น มีการ down server บ่อยก็ต้องมานั่งทำงาน
ซ้ำ ๆ แบบนี้หลายรอบ พาลจะทำให้หลุดได้ ในกรณีที่มี application มากมายก่ายกอง บนนั้น

ในส่วนของ ubuntu หากเราต้องการให้ เมื่อ server มีการ run process ที่เราต้องการ ในตอนที่ server มี
การ start ขึ้นามาใหม่ ขั้นแรก ก็ให้เขียน shellscript file ที่ใช้สำหรับ start application ไว้ สักที่
เช่น สร้างเป็น(ในส่วนนี้ยังไม่ยกตัวอย่าง shellscript นะครับ ยังไม่ประเเด็น)

file : /home/user/script_start_app.sh

หลังจากนั้น ก็เข้ามายุ่งกับ

file : /etc/rc.local


เรียกมาแก้ไขโดย คำสั่ง

$ sudo vi /etc/rc.local

หลังจากนั้น ก็จะมี request password ให้ใส่ ก็ทำการใส่ก็จำเข้ามาหน้า edit ครับ
ที่ต้อง sudo เนื่องจากเป็นระบบหลักครับ อาจทำให้ ระบบเสียหายได้ถ้ามีการแก้ไข เปลี่ยนแปลง
คนแก้ไขจึงต้องเป็น admin เพื่อรับรองความปลอดภัย ระดับนึงครับ

ก็เพิ่ม recode ตามนี้ไป

echo 'start : /home/user/script_start_app.sh'
/home/user/script_start_app.sh >> /home/user/log/scriptstart.log

exit 0


** ในส่วน >> /path/logfile คือการให้ ข้อความ ที่เรา echo ไปออกไว้ที่ใหน

หลังจากนั้น ก็จะพบว่า เมื่อทำการ restart เครื่องแล้วก็จะมีการ start script ดังกล่าวเสมอ

เพิ่มเติม
กรณีที่เราไม่ต้องการให้การ start script เป็นสิทธิ root เนื่องจากเรามี user ที่จำเพราะเจาะจง หรือมีการใช้งานหลากหลาย ก็จะเพิ่มเติม ในส่วน script ดังนี้

su -l user -s /script.sh


ตัวอย่างเช่น

echo 'start : /home/user/script_start_app.sh'
su -l user1 -s /home/user/script_start_app.sh >> /home/user1/log/scriptstart.log
su -l user2 -s /home/user/script_start_app.sh >> /home/user2/log/scriptstart.log
su -l user2 -s /home/user/script_start_web.sh >> /home/user2/log/scriptstart.log

exit 0


และ อีกวิธีก็เป็น การใช้ crontab เข้ามาช่วยในการเช็ค ว่า ถ้า process ตาย ก็จะทำการ start โดยเช็คทุก ๆ
กี่นาทีตามกำหนด แต่จะอันตรายกว่า เนื่องจาก ถ้าapplication มี error ที่ร้ายแรง อาจทำให้มีการทำงานผิดพลาด
ซ้ำ ๆ เกิดขึ้น

วันพุธที่ 5 สิงหาคม พ.ศ. 2552

datetime python

ในส่วนนี้จะเป็นตัวอย่างเกี่ยวกับการใช้ datetime ใน python ซึ่งเราจะพบเจอบ่อยมาก

ตัวอย่าง

1.การดึงเวลาปัจจุบัน ของเครื่องขณะนั้น
from datetime import datetime
>>> now = datetime.now()
>>> now
datetime.datetime(2009, 8, 5, 19, 9, 47, 870000)


เรียงลำดับ ก็ได้ได้เป็น
(2009, 8, 5, 19, 9, 47, 870000)
(ปี, เดือน, วัน, ชั่วโมง, นาที, วินาที, หน่วยที่น้อยกว่า วินาที)

2.การแปลงจาก datetime ให้อยู่ใน format
>>> from datetime import datetime
>>> now = datetime.now()
>>> now
datetime.datetime(2009, 8, 5, 19, 20, 14, 90000)
>>> now.strftime("%d/%m/%y %H:%M:%S")
'05/08/09 19:20:14'


detail ตัวแปรต่าง ๆ ก็เทียบจากตารางข้างล่างนี้ครับ
อ้างอิง : 9.1.7. strftime() Behaviorl

3.การแปลงจาก str เป็น datetime ด้วย format
>>> from datetime import datetime
>>> dt = datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M")
>>> dt
datetime.datetime(2006, 11, 21, 16, 30)


4.การเลื่อน วัน-เวลา
>>> from datetime import datetime, timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2009, 8, 5, 19, 9, 47, 870000)

>>> d1 = timedelta(days=1)
>>> now + d1
datetime.datetime(2009, 8, 6, 19, 9, 47, 870000)

วันพฤหัสบดีที่ 30 กรกฎาคม พ.ศ. 2552

python encode decode hex string example

วันนี้ลองหยิบ code ในการเข้ารหัสตัวอักษร ไว้สำหรับทำงาน, ส่ง parameter
หรือใช้ทำอะไรอีกต่าง ๆ มามายให้ดูกันครับ

งานส่วนใหญ่ที่ใช้การ encode นี้ส่วนใหญ่จะใช้เพื่อแปลงภาษาไทย เป็นส่วนใหญ่
เนื่องจากภาษาไทยนั้น ไม่ใช่พื้นฐานของภาษาที่คอมพิวเตอร์จะถอดรหัสได้ทันที และ
มาตรฐานที่มากมายในการเข้าถึง และแปลงรหัส เพื่อให้ได้ ภาษาที่ถูกต้อง

ตัวอย่างเล็ก ๆ เอาไปลอง run แล้วพอจะเห็นภาพนะครับ ...

# -*- utf-8 -*-

def encode32(src):
    return ''.join(["%06x" % ord(x) for x in src])

def encode16(src):
    return ''.join(["%04x" % ord(x) for x in src])

def encode16char(src, ch='%'):
    return ''.join(['%(ch)s%(i)s%(ch)s%(j)s' %{'i':("%04x" % ord(x))[:2].upper(), 'j':("%04x" % ord(x))[2:].upper(), 'ch':ch}  for x in src])

def encode8(src):
    return ''.join(["%02x" % ord(x) for x in src])

def decode16(src):
    n = (len(src)+1)/4
    return "".join( [unichr(int(src[i*4:i*4+4],16)) for i in range(n)])

def isthaistr(s):
    try:
        s = s.decode('tis-620')
    except:
        pass
    return any( ord( c ) > 255 for c in s )

def encode8hexcharx(x, c='%'):
    if isthaistr(x):
        try:
            x = x.decode('tis-620')
        except Exception, e:
            pass
        x = x.encode('utf-8').encode('hex')
        return  '%(ch)s%(i)s%(ch)s%(j)s%(ch)s%(k)s' % {'i':x[:2].upper(), 'j':x[2:4].upper(), 'k':x[4:].upper(), 'ch':c}
    else:
        return x

def encode8hexchar(src, ch='%'):
    return ''.join([encode8hexcharx(x, c=ch) for x in src])

if '__main__' == __name__:
    a = '00740065007300740e010e020e03'
    s = decode16(a)
    print 'decode16 %s >> %s' % (a, s)
    a = s
    s = encode16char(a)
    print 'encode16char %s >> %s' % (a, s)
    a = u'abc กขค 123'
    s = encode8hex(unicode(a), ch='-')
    print 'encodeutf8hex %s >> %s' % (a, s)

ส่วนอันนี้ก็เป็น ผลที่ได้จาก test นะครับ

decode16 00740065007300740e010e020e03 >> testกขฃ

encode16char testกขฃ >> %74%65%73%74%0E%01%0E%02%0E%03

encodeutf8hex abc กขค 123 >> abc -E0-B8-81-E0-B8-82-E0-B8-84 123

วันจันทร์ที่ 29 มิถุนายน พ.ศ. 2552

turbogears 2 and database

project ส่วนใหญ่นั้น จะมีการเก็บข้อมูลไว้เพื่อใช้อ้างอิงหรือออก report จึงทำเป็นต้องมี database เพื่อเก็บข้อมูล
ในส่วนของ turbogears 2 ในบทความนี้ จะใช้ session ครับ โดย tg2 นี้จะเตรียมไว้ให้แล้ว
ใน project/package/model/__init__.py คือ DBSession
คราวนี้ก็มาเตรียม model กัน สิ่งที่ต้องทำสองอย่างคือ

----------------------------------------
setup
----------------------------------------
1. สร้าง class เพื่อจับคู่กับ database

class Model1(object):
  pass

class Model2(object):
  def __init__(self, **kw):
    name = kw.get('name')
    sex = kw.get('sex')

------------------
2. ทำการเขียน mapper ใน def init_model(engine):
from sqlalchemy.types import String, Integer
from sqlalchemy import Sequence
ของ file : project/package/model/__init__.py

  model1_table = Table('model1', metadata, autoload=True, autoload_with=engine)
  mapper(Model1, model1_table)

  model2_table = Table('model2', metadata, Column('id', Integer, Sequence('id_seq'),primary_key=True), useexisting=True, autoload=True, autoload_with=engine)
  mapper(Model2, model2_table)

------------------
3. set database ที่ development.ini เพิ่ม

sqlalchemy.url ={db_name}://{user}:{password}@{db_host}:{db_port}/{db_service_name}


----------------------------------------
query
----------------------------------------
ในส่วนของคำสั่งต่าง ๆ จะอยู่ภายใต้ controller และมี @expose() กำกับบน function ทุกครั้ง
การดึงข้อมูลแบบอ้าง model
------------------
ในส่วนแรกจะเป็นการดึงข้อมูลด้วย Id

model1_model = DBSession.query(Model1).get(1)

------------------
ในส่วนแรกจะเป็นการดึงข้อมูลด้วย name หรือ อื่น ๆ

model2_model = DBSession.query(Model2).filter(Model2.name=='test').first()

------------------
ในส่วนแรกจะเป็นการดึงข้อมูลด้วย name หรือ อื่น ๆ ที่มากกว่า 1 field
from sqlalchemy import and_

model2_model = DBSession.query(Model2).filter(and_(Model2.name=='test', Model2.sex=='male')).first()

------------------
ตัวอย่าง code complex and join

form datetime import datetime
now = datetime.now()
votepoint_model, votejob_model = DBSession.query(VotePoint,VoteJob).filter(
and_( VotePoint.dial_digit_cmdid==kw['dialdigitcmd_model'].id, VotePoint.dial_digitid==kw['dialdigit_model'].id,
VotePoint.vote_jobid==VoteJob.id,
VoteJob.start_dttm<=now,
VoteJob.expire_dttm>=now)
).first()

kw.setdefault('votepoint_model', votepoint_model)
kw.setdefault('votejob_model', votejob_model)

ในที่นี้จะเห็นได้ว่า สามารถนำตัวแปรสองตัวมารับได้เลย ซึ่งสะดวกต่อการใช้งานมาก
------------------
การกระทำข้อมูล ด้วย sql qrery โดยตรง

DBSession.execute("update model1_point set name = 'test2' where id=:id", {'id':1})
DBSession.flush()

ในส่วนของการ flush ก็จะเหมือนการ commit ไปยัง database แล้ว

วันพฤหัสบดีที่ 11 มิถุนายน พ.ศ. 2552

set oracle client for turbogears2

ให้ทำการ download
Oracle Database 10g Express Edition Client
ได้ที่
http://www.oracle.com/technology/software/products/database/xe/htdocs/102xewinsoft.html
มาเพื่อเป็นเหมือนตัวแทน oracle client
ในที่นี้ไว้ที่ drive d: ก็จะได้

D:\XEClient\bin

สามารถเปลี่ยน directory ได้ตามความพอใจครับ

ทำการ set

ORACLE_HOME = D:\XEClient

ได้ที่
คลิ๊กขวาที่
My Computer >> properties >> System Properties >> Advanced
กดปุ่ม
Environment Variables
จะ set ที่ User หรือ System Valiables ก็ได้ครับ

---------------------------
หลังจากนั้น ก็ทำการติดตั้ง cx_Oracle เป็น utility สำหรับ ติดต่อ Oracle สำหรับ python
ไปยัง web
http://cx-oracle.sourceforge.net/
เลือก download ที่:
Windows Installer (Oracle 10g, Python 2.5)

หลังจากนั้นให้ทำการติดตั้ง โดยให้เลือก directory ที่เราเคยติดตั้ง tg2env ครับ
โดย default ตัวติดตั้ง รู้สึกจะไปที่ python25 ให้ทำการเปลี่ยนครับ เพราะเราไม่ได้ทำให้ python
แต่กำลังทำบน environment turbogears2

เมื่อติดตั้งเสร็จ ภายใต้ folder tg2env จะมี cx_Oracle-doc เพิ่มเข้ามา ถ้าลง ผิดก็ลงใหม่ครับ
เมื่อเลือก Install อีกทีมันจะถามว่า จะ Repair หรือ Remove

เมื่อเสร็จแล้วก็ลองเข้าโดย

cd tg2env/Scripts
activate.bat

python
>> import cx_Oracle
>>


ไม่ขึ้น error ก็ใช้ได้ครับ

---------------------------

set ให้ trubogears2 ติดต่อ Oracle Database
ให้แก้ไขที่ file development.ini
ทำการ comment บรรทัดต่อไปนี้ ด้วยเครื่องหมาย # :

#sqlalchemy.url = sqlite:///%(here)s/devdata.db

เพิ่ม บรรทัดนี้เข้าไป :

sqlalchemy.url =oracle://user:password@61.47.7.241:1521/xe


---------------------------

กรณี database set เป็น ascii หรือ อื่น ๆ ที่ไม่ใช่ utf-8
(แต่ถ้าเป็น utf-8 อยู่แล้วก็อาจไม่ต้องทำครับ)
เราจะเป็นต้องเปลี่ยน language oracle เครื่องเราให้อ่านภาษาไทยออก
โดยการ set registry ที่ run พิมพ์:

regedit

จะขึ้น Registry Editor ขึ้นมา เลือกที่

HKEY_LOCAL_MACHINE >> SOFTWARE >> ORACLE >> ??? >> NLS_LANG

ไม่แน่ใจว่า ภายใต้ folder oracle อาจมี sub folder ย่อยไปอีก อาจต้องตามเข้าไปหาในนั้น
เปลี่ยน ค่าใน

NLS_LANG = .UTF8

set ค่าให้กับ turbogears2 ผ่านทาง sqlalchemy เพิ่ม:

sqlalchemy.convert_unicode=True
sqlalchemy.encoding='utf-8'

---------------------------

install turbogears2 on windows

ขั้นตอนแรก ให้ทำการ install python version Python 2.5.4 ก่อนครับ
เนื่องจาก turbogears 2 นั้น run ด้วย python แต่อย่าเกิน version 2.5 ครับ เพราะ ไม่ support

โหลดที่:
http://www.python.org/download/

ทดลองว่าเสร็จแล้วโดยลองพิมพ์:

$ python --version
Python 2.5.4

ทำการ download ez_setup.py โหลดที่:
http://peak.telecommunity.com/dist/ez_setup.py

พิมพ์ command:

$ python ez_setup.py

ทดลองว่าเสร็จแล้วโดยลองพิมพ์:

$ python
>>> import setuptools
>>> setuptools.__version__
'0.6c9'

ในส่วนของ python บน windows ให้ทำการ win32api โหลดที่:
http://starship.python.net/crew/mhammond/win32/
หรือ
http://sourceforge.net/project/showfiles.php?group_id=78018

file น่าจะประมาณนี้ : pywin32-213.win32-py2.5.exe
คือควรเป็น python 2.5 ครับ

เริ่มทำการ create environment โดยพิมพ์

$ easy_install virtualenv

ทำการ download tg2-bootstrap.py โหลดที่:
http://www.turbogears.org/2.0/downloads/current/tg2-bootstrap.py

พิมพ์ command:

python tg2-bootstrap.py --no-site-packages tg2env

---------------------------------------
เริ่มสร้าง project แรก
ในส่วนที่เพิ่งติดตั้งเสร็จไป เข้าไปที่ folder

tg2env/Scripts

พิมพ์ command:

$ activate.bat

ก็จะมี (tg2env) นำหน้า เพื่อให้รู้ว่ามีการ set environment turbogears2 เรียบร้อย

จากนั้น ก็ทำการสร้าง project โดย ไปยัง directory ที่ต้องการวาง project เริ่มด้วยคำสั่ง:

$ paster quickstart app-name

ในที่นี้ใส่ project ชื่อ app-name สามารถเปลี่ยนได้ครับ
จะมีคำสั่งขึ้นว่า

Enter package name [appname]: pkg-name

ในที่นี้ใส่ package ชื่อ pkg-name สามารถเปลี่ยนได้ครับ
จะมีคำสั่งขึ้นว่า

Do you need authentication and authorization in this project? [yes]

ก็กด enter เลยครับ

---------------------------------------
ทดสอบ start project แรก
ในที่นี้ใส่ project ชื่อ app-name ก็เข้าไปยัง folder นี้
หลังจากนั้น

พิมพ์ command:

paster serve development.ini --reload

ในส่วนของ --reload นั้น จะพิมพ์หรือไม่ก็ได้ครับ แต่มีไว้ ก็จะทำให้เกิดการ
restart ทุกครั้งเองที่มีการแก้ไข file ใน project

ในกรณีที่มีการย้าย project ไปยังเครื่องอื่น หรือ มีการเปลี่ยนแปลง env
ให้เข้าไป folder app-name
แล้วใช้คำสั่ง

python setup.py install

หรือ

python setup.py develop

---------------------------------------

วันพฤหัสบดีที่ 28 พฤษภาคม พ.ศ. 2552

Python direct function by string

สักวันหนึ่งเราคงได้พบว่า การเขียน if else เพื่อเลือกเรียก function นั้น เป็นเรื่องรกและสิ้นเปลืองพลังงาน แถมยังทำให้ code ไม่สวยงาม
ในส่วนของ post ชุดนี้จึงนำเสนอวิธีการเรียก function ด้วย string มาแต่จำเป็นที่ string นั้น จะต้องเป็นชื่อเดียวกับ function นะครับ โดยตัวอย่างนี้มีอยู่สองวิธีครับ ตามด้านล่างเลย

สมมุติ code มีอยู่ว่า

class Test(object):
    def __init__(self):
        pass
    def a(self, data):
        print '--a-- %s' %str(data)

    def b(self, data):
        print '--b-- %s' %str(data)

    def c(self, data):
        print '--c-- %s' %str(data)

    def call(self, fname, data):
        if 'a' == fname:
            self.a(data)
        elif 'b' == fname:
            self.b(data)
        else:
            self.c(data)

1. การใช้ eval() << เหมาะกับการใช้เรียกแบบตรง ๆ ซึ่งไม่จำเป็นว่า function ที่เรียกต้องอยู่ใน Object

def call(fname, data):
try:
eval('self.%s(data)' %fname)
except:
self.c(data) #default call function c

2. การใช้ getattr() << สะดวกมาก แต่ใช้ได้แต่กับ object เพราะต้องอ้าง attribute

def call(fname, data):
getattr(self, fname, 'c')(data)

และในกรณีใด ๆ ที่เราต้องการอ้างถึง object ตัวอื่น ก็สามารถแทนที่ของ self ครับ ไม่จำเป็นว่าต้องเรียกตัวเองเสมอไป

ค่อยข้างจะมีประโยชน์คนละแบบครับ แล้วแต่การใช้งาน

วันอังคารที่ 26 พฤษภาคม พ.ศ. 2552

Lighttpd change http port and path url

Lighttpd เป็น tool ตัวหนึ่งในการช่วยจัดการกับ การเข้าถึง web, directory, load balance, block path
ดู ๆ ข้างในแล้ว เหมือนจะทำขึ้นเพื่อ php มากเลย -.-"
(เอาเป็นว่ารายละเอียดไว้ทีหลัง การติดตั้งหรืออะไรต่าง ๆ หรือไปอ่านเองก่องเถอะครับ เหอ ๆ ว่าง ๆ คงได้เอามาลงครับ)

ตอนนี้จะเป็นการ เปลี่ยน path url การเข้าถึง และ เปลี่ยน port url พร้อม ๆ กัน
(สถาณการณ์บีบบังคับ ทำให้งานออกมาได้จิง ๆ ไม่ต้องรอ บริ้วววว อารมณ์)

งานคือ url ที่ client เรียกเข้าถึงเป็น

http://www.vserver.com/gw/mo/oper1

server ตั้งอยู่ที่ (url จริง ในการใช้งาน)

http://www.vserver.com:8081/req/oper1

ในส่วนของ oper นั้น จะมี oper1, oper2, oper3 ครับ

ขึ้นตอนแรก เข้าไปหา file ชื่อว่า

/etc/lighttpd/lighttpd.conf

เผอิญว่า ทางพี่ ๆ เค้าลงไว้บน ubuntu ให้เสร็จแล้ว ไม่แน่ใจนะครับว่าอยู่ pathนี้ตลอดเปล่า

ขึ้นที่สอง ก็ไปเพิ่ม หรือเอา comment ออกครับในส่วนของ
(comment คือ เครื่องหมาย # หน้าบรรทัด หรือ ข้อความ)

server.modules = (
   "mod_xxxxxxxx", # << 'xxxxxxxx' อันนี้ละไว้ว่าอันอื่น ๆ ครับ
#   "mod_xxxxxxxx", # << แบบนี้คือ comment
   "mod_rewrite",
   "mod_redirect",
   "mod_proxy"
)


ขั้นที่ 3 นั้น แบ่งเป็น 2 ส่วนครับ
3.1 ใช้แบบ redirect
กรณีนี้จะสั้นครับเหมาะกับการเข้าถึง หรือ ใช้ http method get ครับ

#-----------------------------------
$HTTP["host"] == "www.vserver.com" {
  url.redirect = ("^/gw/mo/(.+)" => "http://www.vserver.com:8081/req/$1")
}
#-----------------------------------


$HTTP["host"] == "www.vserver.com" { # << เป็นเหมือนคำสั่งใช้ในการ if คือ ถ้า url เป็นชื่อนี้ก็จำเข้ามาทำงาน

(.+) # << เป็นการแทนค่า path url และ parameter ที่ต่อท้าย เพื่อนำมาแทนใน $1 และสามารถกำหนดได้มากกว่า 1 ตัว เช่น ข้อมูลมาในแบบ rest คือ /gw/mo/(.*)/(.*)/(.*) => /gw/mo/$1?id=$2&price=$3 เป็นการแปลงเป็น name value ธรรมดาครับ

3.2 ใช้แบบ rewrite
กรณีนี้จะต้องทำสองอย่างครับ คือ เปลี่ยน path url ก่อน แล้วค่อย proxy ยัง post ที่ต้องการครับ เนื่องจาก 3.1 นั้นใช้ได้แต่เฉพาะ http method get ถ้าจะใช้ http method post ก็คงเขียนยาวหน่อยครับ

#-----------------------------------
$HTTP["host"] == "www.vserver.com" {
  url.rewrite-once = ("^/gw/mo/(.*)$" => "/req/$1")
  $HTTP["url"] =~ "^/req" {
    proxy.server = (
      "" => (
        "vserver" => (
        "host" => "127.0.0.1",
        "port" => 8081,
        "fix-redirects" => 1
        )
      )
    )
  }
}
#-----------------------------------


เสร็จแล้วก็ทำการ restart ด้วยคำสั่ง

#-----------------------------------
$ /etc/init.d/lighttpd restart
#-----------------------------------


ก็เป็นอันเรียบร้อยครับ
อ๋อ ในกรณี post data xml
syntax จะ ประมาณนี้
<xml> <data> data 123 abc... </data> </xml>

เมื่อทำการ post ให้ใส่ content type : text/xml ด้วยครับ
ไม่ใช่นั้น ปีกกว่าจะเปลี่ยนไป จะได้ประมาณนี้ครับ
%3Cxml%3E %3Cdata%3E data 123 abc... %3C2Fdata%3E %3C%2Fxml%3E

จบแต่เพียงเท่านี้ -.-" กว่าจะหาเจอ ตอนแรกไม่รู้ว่ารวมเป็น combo ได้

วันพุธที่ 13 พฤษภาคม พ.ศ. 2552

python yield

เห็นแว๊บ ๆ สำหรับ keyword 'yield' สำหรับ python ก็มานั่งดูว่าใช้ประโยชน์อะไรได้บ้าง พอดีเห็นตัวอย่างง่าย ๆ เอามาทำความเข้าใจ เหมือนกับว่า มันจะใช้ช่วยในการ return ข้อมูลออกมา จาก function ที่กำลังดำเนินการาอยู่ ดูตัวอย่างดีกว่า

def odds(limit):
    i = 1
    while True:
        if limit > i
            break
        print 'in odds : > %s' % str(i)
        yield i
        i += 2

for i in odds(5):
    print 'odds : %s' % str(i)

## return
in odds : > 1
odds : 1
in odds : > 3
odds : 3
in odds : > 5
odds : 5


function ดึงแต่เลขคี่ ที่ค่าไม่เกิน limit
จากที่เห็น ก็จะเห็นว่า จะมีการเรียกเข้า function odds ซึ่งยังทำการเทียบข้อมูลไม่ถึง limit ที่ตั้งไว้ก็จะมีการ return ค่าออกมาให้ function เรียกใช้งานทำงานไปก่อน

ซึ่งจะต่างจาก function ธรรมดาทั่วไป ที่ทำการ return function เมื่อทำงานเสร็จสิ้น ตามตัวอย่างนี้

def odd2(limit):
    r = []
    i = 1
    while 1:
        if i > limit:
            break
        print 'in odd2 : > %s' % str(i)
        r.append(i)
        i += 2
        return r

for i in odd2(5):
    print 'odd2 : %s' % str(i)

## return
in odd2 : > 1
in odd2 : > 3
in odd2 : > 5
odd2 : 1
odd2 : 3
odd2 : 5


โดย ดู ๆ แล้ว ในส่วนของ yield น่าจะใช้กับ function loop ที่ต้องการทำงานกับ ตัวแปร ภายนอก ที่มีการเปลี่ยนแลงเรื่อย ๆ


มาเพิ่มตัวอย่างอีกสักสองอันครับ สำหรับ สุดยอด function นี้
อันแรกเป็นการเขียนเพื่อจับคู่ loop แต่ละ list ครับ
def for_2list(l1, l2):
    for i1 in l1:
        for i2 in l2:
            yield i1, i2
เวลาเรียกใช้
ll1 = ['a', 'b', 'c']
ll2 = [1, 2, 3]

for a1, a2 in for_2list(ll1, ll2):
    print ' >> %s, %s' %(a1, a2)

ผลลัพท์
>> a, 1
>> a, 2
>> a, 3
>> b, 1
>> b, 2
>> b, 3
>> c, 1
>> c, 2
>> c, 3

มาเพิ่มเติมกันอีกสักอันครับ สำหรับตัวอย่าง แจ่ม ๆ ที่น่าจะได้ใช้กันบ่อย
คือ loop list ทีละ pack โดยกำหนด pack ได้เองว่าทีละเท่าไร

def for_list_pack(list, size):
    i=0;j=size
    while(True):
        if j > len(list):
            j = len(list)
        data = list[i:j]
        yield data
        if j == len(list):
            break
        i=j;j=i+size

เวลาเรียกใช้
l1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

for p in for_list_pack(l1, 3):
    print ' >> %s ' %p

ผลลัพท์

>> [1, 2, 3]
>> [4, 5, 6]
>> [7, 8, 9]
>> [0]


จริง ๆ เอาไปใช้งานได้เลยแหละครับ ^_^

วันจันทร์ที่ 11 พฤษภาคม พ.ศ. 2552

วันศุกร์ที่ 8 พฤษภาคม พ.ศ. 2552

python cut string by count to list

ตัวอย่างเล็ก ๆ เกี่ยวกับการ ตัดข้อความทีละจำนวนตัวอักษรที่เรากำหนด
m = 'abcdefghijklmnopqrstuvwxyz0123456789'
x = '10'

l = [m[i:i+x] for i in range(0, len(m), x)]
print l

result
['abcdefghij', 'klmnopqrst', 'uvwxyz0123', '456789']

ประโยชน์ก็อาจเพื่อเอาไว้สำหรับ ตัดข้อความขึ้นบรรทัดใหม่ หรือ แบ่งข้อความเพื่อ
จัดส่งทีละชุดอย่างเป็นระเบียบ แล้วแต่ความต้องการ

หวังว่าคงมีประโยชน์บ้างน่อ ^_^

วันพฤหัสบดีที่ 7 พฤษภาคม พ.ศ. 2552

python check message is thai language

code แบบง่าย ๆ เอาไปใช้ก่อน
#function long

def isthaistr(s):
    for i in s:
        if 255 < ord(i):
            return True
    return False
#function short

def isthaistr(s):
    return any( ord( c ) > 255 for c in s ) 
ในกรณีที่ข้อความเราได้มานั้น เป็น TIS-620 เราอาจต้องแปลงเป็น UTF-8 โดย encode มันด้วยสำสั่งแบบนี้
s = 'ก'
print ord(s) # 161
s = s.decode('its-620')
pirnt ord(s) # 3585

s = 'a'
print ord(s) # 97
s = s.decode('tis-620')
print ord(s) # 97
ประมาณนี้...

python multi decorator

เนื่องจากเมื่อครั้งก่อนหน้านี้ ได้สร้าง decorator ในหัวข้อ python decorator แล้วบังเอิญว่า ไม่ได้ใช้ พอมาจับอีกทีถึงรู้ว่า คราวที่แล้ว ที่กำหนด decorator เป็น class นั้น มัีนค่อยข้างบีบความสามารถ ไว้ถ้าว่าง ๆ คงไปนั่งแก้และหาวิธีแก้ไขเพิม เนื่องเติมจาก ช่วงนี้ มีการเขียน code ที่ต้องเกี่ยวข้องกับระบบอื่น ๆ มากมาย หรือเรียกว่า ในส่วนที่ทำเป็น adaptor ก็ถูก

เอาเป็นว่าเข้าเรื่องดีกว่า ไม่รู้โม้ทำไม

เรื่องมีอยู่ว่า เนื่องจากในส่วนของ decorator ของ python นั้น มันค่อนข้างจะนำมาประยุกต์ใช้งาน ทำให้ code ที่ต้องเขียนซ้ำ ๆ กันน้อยลง นั่นคือ การใช้ @(decorator) ในการครอบ function บาง function โดยไม่ต้องเขียนให้เห็น ๆ กะตา

แต่ ถ้าเราต้องการมากกว่านี้หละ ครอบหลาย ๆ ชั้น เมื่อครั้งที่แล้วใช้ class ไป จึงรู้ว่า ในส่วนของ decorator นั้น เวลาทำงานมันจะเรียก function ที่อยู่ถัดจากมัน ดังนั้น ถ้าเกิดเราเขียน decorator เป็น class หละก็ เท่ากับว่า เรากำลังปิดทางทำมาหากินเราในอนาตนต่อไป ในหัวข้อนี้จึงเป็นการแนะำนำการเขียน decorator เป็น function เพื่อ ทำการ ซ้อน function เข้าไป เยอ ๆ สนุกดี

เริ่มเลย ...

decorator แรกนี้เป็นในส่วนทำการแสดงผล หลังจากทำงานเสร็จ เพื่อให้อยู่ใน format
def get_result(f):
    def wrap_func(*arg, **kw):
        try:
            f(*arg, **kw)
            return {'status_code':'200', 'status_description':'Success'}
        except Exception, e:
            return {'status_code':'500', 'status_description':str(e)}
    return wrap_func
ส่วนที่สอง เป็นในส่วน check parameter
def req_param(*fields):
    def req_paramn(f):
        def wrap_func(*arg, **kw):
            for field in fields:
                if not kw.has_key(field) or '' == kw.get(field).strip():
                    raise Exception('parameter %s not empty.' % field)
            return f(*arg, **kw)
        return wrap_func
    return req_paramn
ส่วนสุดท้าย function ที่ทำงาน
@sendcont_result
@req_param('ntype', 'to', 'from', 'message')
def on_call(**kw):
    try:
        if 'DTAC' == kw.get('ntype'):
            on_dtac(**kw)
        elif 'AIS' == kw.get('ntype'):
            on_ais(**kw)
        else:
            raise Exception('not support ntype %s.' % str(kw.get('ntype')))
        return kw
    except Exception, e:
        raise e
เวลาเรียกใช้ก็
res = on_call({'ntype':'AIS', 'message':'hi python.', 'to':'you', 'from','i'})
ผลลัพท์ 'res' ถ้าไม่เกิด error เราก็จะเป็น
status_code = 200
นอกนั้น ก็
status_code = 500

เช่น ในกรณีที่เราไม่ได้ใส่ ntype เข้ามา ก็จะฟ้องว่า
status_code = 500
status_description = parameter ntype not empty.
หรือ กรณี method on_dtac หรือ on_ais มีปัญหา ก็จะแสดงคล้ายข้างบนครับ

ตัวอย่างเท่านี้ ที่เหลือก็ขึ้นอยู่กับการประยุกต์ครับ

วันอังคารที่ 31 มีนาคม พ.ศ. 2552

Ubuntu set static ip address

เนื่องจาก เวลาที่เราต้องการนำ web หรือ application ไปไว้ยัง server เพื่อเป็น service บางอย่างนั้นจำเป็นต้องให้ ip address ของเครื่อง server ที่เราต้องการนั้น ไม่ถูกเปลี่ยนแปลง เมื่อมีการ shutdown หรือเปลี่ยนแปลง ระบบเครือข่าย ในส่วนของ ubuntu ก็จะมีวิธีการประมาณนี้ครับ

เข้าไปแก้ไข file ที่ทำการ setup ip โดยจำเป็นต้องใช้สิทธิ root ครับ ตามด้านล่างนี้ ...

$ sudo vi /etc/network/interfaces

เมื่อเข้าไปครั้งแรกจะเห็นแค่ สองบรรทัด ...

auto lo
iface lo inet loopback

ก็ทำการเพิ่ม

auto eth1
iface eth1 inet static
address 192.168.123.248
gateway 192.168.123.254
netmask 255.255.255.0
network 192.168.123.0
broadcast 192.168.123.255

ในส่วนของ eth1 ในที่นี้จะเป็นเหมือนตัวแทน hardware wireless ในการเชื่อมต่อของเครื่อง
ในส่วนของ address นั้น จะเป็น ip address ที่เราต้องการ force ไว้
ส่วนที่เหลือ นั้นก็เป็นองค์ประกอบ network องค์กร ครับ ผมสมติให้เป็น 192.168.123.xxx ก็ไปไล่เปลี่ยนเอา

สุดท้ายก็ทำการ restart network ด้วย command ...

sudo /etc/init.d/networking restart

น่าจะมี log ขึ้นประมาณนี้

* Reconfiguring network interfaces... [ OK ]

ถ้าไม่ขึ้น error ก็ ...
เป็นอันเสร็จเรียบร้อย


sudo vi /etc/resolv.conf


edit if can't get internet


nameserver 192.168.123.254



end config...

วันพฤหัสบดีที่ 26 มีนาคม พ.ศ. 2552

python receive http post text stream by turbogears2

ในส่วน ของ web controller ของ turbogear นั้น โดยปกติก็จะรับ เป็น kw อยู่แล้ว คือรับ name:value ตาม format

    @expose('templatename')
    def servicename(self, **kw):
        print 'data key, value :', kw
        return dict(page='templatename')

ในกรณีที่เราต้องการ รับ text stream เช่น client อาจ post xml มาเพื่อ information บางอย่างก็จะต้อง ดึงจาก body แทน code ก็จะเพิ่มประมาณนี้ครับ

import pylons

    @expose('templatename')
    def servicename(self, **kw):
        body = pylons.request.body
        print 'data text stream : %s' % body
        return dict(page='templatename')

python http post text stream

ในกรณีที่เราต้องการ post text string หรือ data บางอย่าง เช่น xml information บางอย่าง
ในส่วนของ การ post ส่วนใหญ่จะเป็นในกรณี name:value โดยตัวอย่าง format จะเป็นประมาณ
http://localhost:8080web?name=uname&data=udata
name:value จะเป็น name:uname , data:udata

แต่กรณีที่เราต้องการ post text อย่างเดียว จะเป็นการ post body ตัวอย่างด้านล่าง

http://localhost:8080/web
body = '<xml><auth><user>userxy</user><pass>xxxyyy</pass></auth><data>test data stream.</data></xml>'

data ในส่วนของ body จะไม่ถูกต่อท้าย และจะไม่มี name:value

code ตัวอย่าง

import httplib

def post(url='', data=''):
    print ' url post : %s ' % url
    print ' data post : %s ' % data
    try:
        ufull = url.replace('http://', '')
        x = ufull.find('/')
        h = httplib.HTTP(ufull[:x])
        h.putrequest('POST', ufull[x:])
        h.putheader('content-type', 'text/xml')
        h.putheader('content-length', str(len(data)))
        h.endheaders()
        h.send(data)
        errcode, errmsg, headers = h.getreply()
        res = h.file.read()
    except Exception, ex:
        raise ex
    return res

if '__main__' == __name__:
    u = 'http://localhost:8080/web'
    d = '<xml><auth><user>userxy</user><pass>xxxyyy</pass></auth><data>test data stream.</data></xml>'
    res = post(url=u, data=d)
    print 'response post : %s' % res
---------------------------------

วันพุธที่ 25 มีนาคม พ.ศ. 2552

python one line code style

python เป็นภาษา script นั่นคือ ส่วนใหญ่จะมี syntex ที่ค่อยข้างอ่านง่าย และกระชับ และใช้งานได้ทันที
การเขียน คำสั่งเล็ก ๆ ในบรรทัดเกียวก็จึงเป็นเรื่องง่าย

ตัวอย่าง code 1
mstr = 'data1,data2  ,  data3,'
l = [m.strip() for m in mstr.split(',') if '' != m.strip()]
print l
output:

['data1', 'data2', 'data3']

# ตัวอย่างเป็นการแยก data ออกจากกันด้วยเครื่องหมาย ',' return เป็น list และตัดช่องว่างออก
# strip() คำสั่งตัด space
# split() คำสั่งแยก string เป็น array ด้วย argument
# [] เครื่องหมาย การประกาศตัวแปร ให้ทำการ return เป็น array

...

วันจันทร์ที่ 23 มีนาคม พ.ศ. 2552

activemq python listener(receive) with stomp.py

ในส่วนที่แล้วมีการพูดถึง send to queue(jms activemq) ไปแล้ว ในส่วนนี้เป็นการรับข้อมูลจาก queue โดยใช้ listener คือ เมื่อมี data เข้ามายัง queue process ก็จะเริ่มทำงาน
ลักษณะการ connect จะคล้ายกับ send แต่จะต่างกันตรงที่ทำการ add class ที่ทำการ listener เข้าไปครับ ตามนี้

file name : listener.py
import time
import sys
import stomp
import logging
from threading import Thread
    
class MyListener(object):
    def on_error(self, headers, message):
        print 'received an error %s' % message
    def on_message(self, headers, message):
        print 'data of headers : %s' % str(headers)
        conn.ack(headers)
        print 'received a message %s' % message
    
class ReceiveConnection():
    def create_connect(self):
        self.conn = stomp.Connection()
    def create_listener(self):
        self.conn.add_listener(MyListener())
        self.conn.start()
        self.conn.connect()
    def __init__(self):
        self.create_connect()
        self.create_listener()
    
conn = ReceiveConnection().conn
    
class Receiver(Thread):
    def __init__(self):
        print 'receive init...'
        Thread.__init__(self)
    def run(self):
        print 'receive run...'
        try:
            conn.subscribe(destination='/queue/qname', ack='client')
            while 1:
                input = raw_input('\nplease q(Enter) to exit:\n')
                if 'q' == input:
                    break
            conn.disconnect()
        except:
            raise Exception('connection to /queue/qname fail!')
    
if "__main__" == __name__:
    print 'start test...'
    r = Receiver()
    r.setName('t_aisws')
    r.start()
    r.join()
    print 'finish test...'

ในกรณีนี้ จะเป็นแบบ ack client คือ เคลีย data จาก queue หลังจาก message listener มีการทำงาน

อีกกรณีเป็นแบบ ack auto คือ เคลีย data จาก q เมื่อรับข้อความจาก queue มาทันที
ทำการแก้ code จากข้างบน โดย บล๊อก
    #conn.ack(headers)
และ เปลี่ยนจาก
    conn.subscribe(destination='/queue/qname', ack='client')
เป็น
    conn.subscribe(destination='/queue/qname', ack='auto')
สองจุดเพียงเท่านี้ ครับ
จบ ๆ ๆ

activemq python send with stomp.py

การทำงานที่มีความซับซ้อนขึ้น และ เป็นงานประเภท transaction มาก ส่วนใหญ่จะมีการใช้ queue มาทำการแบ่ง process ต่าง ๆ ออกจากกัน ในส่วนของผู้ที่เคยเขียน java มานั้น คงรู้จัก JMS ดีกันอย่างดี หรือพอสมควร
ทำไมไม่ใช้ queue ของ python ? เนื่องด้วย ผมก็มองว่า ภาษา python เป็น ภาษาที่ใช้ในการ Integrate เป็นส่วนใหญ่ เนื่องจากแก้ไขได้ง่าย รวดเร็ว ดู code สะอาดตา แค่นี้ก็พอแล้วสำหรับ python ส่วน java นั้น จะเป็นภาษาที่มีความแข็งแรง และ เสถียรภาพค่อนข้างสูง ถ้าไม่เขียน bug เยอนะครับ
stomp คือ อะไร ? ก็คือ protocal ที่ใช้ในการติดต่อ ประเภทหนึ่ง เป็น text stream
รายละเอียดหาได้จาก http://stomp.codehaus.org/

เกริ่นพอแระ เริ่มดีกว่า

ก่อนอื่นก็ ไป download stomp.py ได้จาก
http://stomp.codehaus.org/Python

เริ่มด้วย การ send message
file name : adaptor.py
import stomp

class adaptor(object):
    def __init__(self, host='localhost', port='61613',  dest='test'):
        print 'initial connection to queue host:', host, ', port:', port, ' for:',  dest
        self.dest = dest
        try:
            self.conn = stomp.Connection([(host, int(port))])
            self.conn.start()
            self.conn.connect()
        except Exception, e:
            raise Exception('fail connect to queue', e)

    def send(self,  msg=' '):
        print 'send to queuename:', self.dest #,' ,message:', ms' ,message:', msg
        while 1:
            try:
                self.conn.send(msg, destination=self.dest)
                break
            except Exception, e:
                if isinstance(e, NotConnectedException):
                self.conn = stomp.Connection([(host, int(port))])
                self.conn.start()
                self.conn.connect()
            else:
                raise Exception('fail send data to queue', e)

#defind pool queue name in qname
adappoolqname = adaptor(host='localhost', port='61613',  dest='/queue/qname')
-------------------------------------

ในกรณีนี้เป็นการเขียน แบบ pool คือ connection ค้างไว้ เพื่อลด header ที่เกิดขึ้นในการติดต่อแต่ละครั้ง และ bug ของ activemq เอง ที่มีปัญหาจากการติดต่อ connection หลายครั้ง ถ้าสงสัยลองทำดูก็ได้ครับ

กรณีนำไปใช้งานจริง ก็จะเป็นประมาณนี้
file name : testadaptor.py
import adaptor

def testsend():
    message = 'test message to q'
    adaptor.adappoolqname.send(msg=message)

if '__main__' == name:
    testsend()
-------------------------------------

วันพฤหัสบดีที่ 5 มีนาคม พ.ศ. 2552

python decorator

Decorator เป็นการเขียน function ครอบ function เพื่อให้สามารถ แยก logic บางอย่าง เช่น validate, connection ต่าง ๆ ออกจาก business ได้

รูปแบบ ก็จะเป็นแบบนี้
function( function( function (data) ) )

function( function( function (data), condition ), condition )
และก็ไม่จำเป็นเสมอไปที่ function ข้างนอกสุดจะทำก่อน มันขึ้นอยู่ตอน เราเขียน decorator
ว่าจะให้ทำอะไรก่อนครับ ตามตัวอย่างได้ อธิบายไว้ ด้านล่าง.

---------------------------------

##Example Decorator class :

## สร้างเพื่อไว้ validate field ที่ต้องการ check ว่าต้องมีส่งมา
## ถ้าไม่มีจะ raise (แจ้ง error) กลับไป ยัง function ที่ทำการเรียก
## และเมื่อ check ว่ามีครบ ก็จะทำการเรียก function ต่อไป
class ReqParam:
    def __init__(self, *args):
        self.fields = args
    def __call__(self, f):
        fileds = self.fields
        def wrapper_func(*arg, **kws):
            dict = kws
            for filed in fields:
                if not kw.has_key(filed) or '' == kw.get(filed):
                    raise Exception('parameter '+filed+' not set.')
            dict = f(*arg, **kws)
            return dict
        return wrapper_func
## จุดสำคัญคือ การว่าในส่วนของ dict = f(*arg, **kws) จะเป็นจุดกำหนดว่าจะทำ function ก่อน
## หรือ Decorator นั้นจะจัดการอะไรบางอย่างก่อนครับ

---------------------------------

##Example function use Decorator :

## ตัวอย่างการเรียกใช้ ในที่นี้เราจะเห็นได้ว่า สามารถมี Decorator ได้มากกว่า 1
    @ReqParam('refno', 'frommsn', 'tomsn', 'msg', 'msgseq')
    def postwap(self, **kw):
        refno = kw.get('refno')
        frommsn = kw.get('frommsn')
        tomsn = kw.get('tomsn')
        msg = kw.get('msg')
        msgindex = kw.get('msgseq')
        print 'refno:', refno, 'from msn:', frommsn, ', to msn:', tomsn, ', message:', msg, ', msgseq:', msgseq
        kw.setdefault('status', 0)
        return kw
## สำหรับการเรียกใช้ function ก็เป็นการเรียก ตามปกติครับ

ไปต่อกันดีกว่าครับที่ decorator ซ้อนหลายชั้น
python multi decorator

---------------------------------

ในส่วนของการ เขียน Decorator นั้น สามารถทำได้ทั้ง แบบ def หรือ class ก็ได้ครับ
ตัวอย่าง :
http://www.python.org/dev/peps/pep-0318/
http://www.artima.com/weblogs/viewpost.jsp?thread=240808

End...

วันพุธที่ 4 มีนาคม พ.ศ. 2552

python script check and replace data key dict() empty

เนื่องจากความง่ายของ python ในการส่ง data เป็น dict ซึ่งจะสามารถทำให้ โยกย้าย หรือแก้ไข script ได้ง่าย และการจัดการ interface ก็แก้ไขน้อยครับ เมื่อเทียบกับ model
แต่ปัญหาก็เกิดขึ้น ถ้ามีการ dev เพิ่มขึ้น ทำ parameter เพิ่มขึ้น การcheck ว่าข้อมูลบางอย่างถูกส่งมาหรือไม่ก็จะเกิดขึ้นมาให้รก code เล่น แต่ก็จำเป็นต้องทำ ถ้าเราไม่ต้องการให้ค่าที่ไม่ได้ส่งมาเป็น None ออกไปยัง function ที่กำลังทำงาน

##Example code function:
my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

## link example from : http://stackoverflow.com/questions/473099/python-how-to-check-if-a-given-index-in-a-dict-exists-yet
-------------------------

##Example code get data :
my_dict = {'a'=1, 'b'='data_b'}

a = kw.get('a') if kw.has_key('a') and int == type(kw.get('a')) else 0
b = kw.get('b') if kw.has_key('b') else ' '
c = kw.get('c') if kw.has_key('c') else 'data_c'
-------------------------

วันจันทร์ที่ 2 มีนาคม พ.ศ. 2552

FileUpload, flash upload multifile Turbogear Python

เริ่มสร้าง ปุ่มสำหรับ การ upload ทีละหลายๆ file บังเอิญว่า ความสามารถ ของ html เพียว ๆ น่าจะไม่สามารถ ถ้าจะใช้ java script ก็จะเยิ้นเย้อ เกินไปหน่ยอ ก็เลยได้ ตัวอย่างนึงมาจาก PHP แล้วก็มาแปลงเอาอ่า

โดย ที่มาจาก :
http://profile.un-no.com/ozone_me/entry-13-Flash+Upload+Multi+File.html
เป็นภาษาไทย ค่อยข้างอ่านรู้เรื่อง จะมีประโยชน์มากถ้าใช้ PHP คือเสนอให้ศึกษาจาก web นี้ก่อนนะครับ ไม่อยากพิมพ์ซ้ำ

จัดการ download :
http://www.codeproject.com/KB/aspnet/FlashUpload.aspx << ยังหา web อื่นไม่เจอครับ แล้วเลือก ที่ Download source code - 475.86 KB
เลือกเอาแค่ file เดียวครับ FlashUpload/FlashFileUpload.swf ที่เหลือไม่ได้ใช้ (.swf เป็น flash file)

แล้วเราก็มาเริ่ม modify กันเลย เพื่อให้ใช้บน Turbogear Python เวอร์ชันที่ใช้ปัจจุบันเป็น version 2
ดังนั้น path ที่แนะนำ

project_folder > package_folder > public > FlashUpload > FlashFileUpload.swf

---------------------------------------------
เพิ่มเติมในส่วน template : uploadfile.html (เพิ่มส่วนนี้ใน body ครับ)

<script language="javascript" type="text/javascript">
function UploadComplete() {
window.location = '/uploadfiles/complete';
}
</script>
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="600" height="370" id="fileUpload" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="/FlashUpload/FlashFileUpload.swf" />
<param name="quality" value="high" />
<param name="wmode" value="transparent"/>
<param name="flashvars" value="&completeFunction=UploadComplete()&fileTypes=*.gif%3b+*.png%3b+*.jpg%3b+*.jpeg&fileTypeDescription=Images&totalUploadSize=2097152&fileSizeLimit=524288&uploadPage=/uploadfiles"/>
<embed src="/FlashUpload/FlashFileUpload.swf" flashvars="&completeFunction=UploadComplete()&fileTypes=*.gif%3b+*.png%3b+*.jpg%3b+*.jpeg&fileTypeDescription=Images&totalUploadSize=2097152&fileSizeLimit=524288&uploadPage=/uploadfiles" quality="high" wmode="transparent" width="600" height="370" name="fileUpload" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"/>
</object>

---------------------------------------------
เพิ่มเติมในส่วน controllers : root.py

...
...
from package.controllers.uploadfile import uploadfilesController

class RootController(BaseController):
  admin = Catwalk(model, DBSession)
  error = ErrorController()
  uploadfiles = uploadFilesController()
...
...

---------------------------------------------
เพิ่มเติมในส่วน controllers : uploadfile.py

from tg import expose
from testtg2web.lib.base import BaseController
class uploadfilesController(BaseController):

  @expose('testtg2web.templates.uploadfiles')
  def index(self):
    return dict(page='uploadfiles')

  @expose('testtg2web.templates.index')
  def testupload(self, *arg, **kw):
    f = kw['Filedata']
    upload = f.file.read()
    name = f.filename[f.filename.rfind('\\')+1:]
    file = open(name,'w')
    file.write(upload)
    file.close()
    print "testupload succedss..."
    return dict(page='index')

  @expose('testtg2web.templates.index')
  def complete(self):
    return dict(page='index')

---------------------------------------------

เผอิญไม่รู้ว่า upload เสร็จแล้วจะแสดงอะไร เลย return index เลย เหอ ๆ
จะแก้ไขเป็น return status ก็ทำ template เพิ่มนะครับ

ประมาณนี้ครับ