2012/06/20

Python 基本開發環境建置

紀錄 python 開發環境的建置過程:

1. 安装 Python-2.7.2
$ tar zxvf Python-2.7.2.tgz
$ cd Python-2.7.2
$ ./configure && make && make install

2. 安装 distribute 0.6.27
$ tar zxvf distribute-0.6.27.tar.gz
$ cd distribute-0.6.27
$ python setup.py build
$ python setup.py install

3. 安裝 pip-1.1
$ tar zxvf pip-1.1.tar.gz
$ cd pip-1.1
$ python setup.py build
$ python setup.py install

4. 安裝 virtualenv 並建制 virtual environment
$ pip install virtualenv
$ virtualenv /opt/env

5. 指定 virtual environment 為預設開發環境
$ source /opt/env/bin/activate
可以看到command line提示改變 (pyenv)brian@localhost:~$
之後用 pip 安裝套件就會放在 /opt/env/lib 之下。


建立獨立的 virtual environment 好處就是比較"乾淨",如果你的系統有多個使用者,大家都裝到系統預設目錄則容易搞混。虛擬環境也可以建立多個,若同時跑多個專案或想切換不同 library 版本也很方便。另一方面也可以釐清開發這個專案"最小需求 library"是哪些,將來要 deploy 時也會比較順利。

2012/06/19

ActiveMQ - 訊息傳遞的仲介

ActiveMQ 是一種 Message-oriented middleware,主要是當作不同 process 或不同主機間的溝通媒介。

會用到 queue 的時機點有很多,就我個人工作上的範疇,通常是
1. Web server 短時間內遇到大量的 request,需要先接起來再由後端子系統處理,達到分散式架構
2. 這件事情本來就沒有很急,丟在 queue 裡面慢慢處理就好


類似 ActiveMQ 的 product 還有 RabbitMQ、ZeroMq,這邊列出了更多種的選擇。我的需求很基本,通常會考量幾個點:
1. 夠不夠 robust
2. Client端有沒有支援熟悉的語言 (C/C++, Java, .Net, Python, Php, Ruby, ...)
3. 文件寫得夠不夠清楚
4. 有在持續維護中

本來想用 C 來連接 ActiveMQ,但發現所有的 C library都沒在維護了,而且也沒 API 文件可以看,是有 C++ 的 ActiveMQ-CPP 可以用,但我不熟lol,最後選擇了 Python 的,pyactivemq也是一段時間沒更新了,重點是編譯不過,stomppy 或許是個不錯的選擇:)

訊息交換方式可以參考 Messaging pattern 裡面定義的,而我常用的也就下面兩種:
1. Push - Pull
2. Publish - Subscribe
這也是大多 product 都支援的

上述講得是 application 層的 ,至於 ActiveMQ 底層的傳輸 protocol 有二
1. STOMP (Streaming Text Oriented Messaging Protocol)
2. OpenWire protocol
差別在哪不是很清楚...只知道 stomp 是個大家熟知的標準,follow就對了!

意外發現一篇好文章,是在 promote ZeroMQ 的:
新世紀通訊函式庫 – ZeroMQ | 程式設計 遇上 小提琴

--

發現 ActiveMQ-CPP 裡面也有提供 C 的 interface,終於不用被 C++ 所擾,在 tar 檔裡面找不到,在 github 上才有,連結在此。

2012/06/11

MySQL 如何避免 SQL injection

遇到這個問題,大部分的人應該會說:用 ORM (Object-relational mapping) 呀!但我就沒有打算多學一套 >< 一方面也是不清楚它欄位的屬性、資料形態是否完全支援 mysql,加上我執行的 query 也蠻複雜的。

先前有找了幾套:
  • SQLAlchemy - 大家比較推薦的
  • Quick ORM - fully compatible with the newest SQLAlchemy

ORM 的好處是你只要設定一次 mapping,之後 select、update、insert 都很方便,但我實在是沒有必要為了一個 injection protect 就用了這麼強大的東西。


除了 ORM 以外...
Search 了一下發現 MySQLdb 本身就有支援以參數的方式來執行 query:
cursor.execute('SELECT * FROM `user` WHERE user=%s AND password=%s;', usr, pwd)
所有的參數都會根據其資料型態來取代 format string,特殊字元自然就會被跳脫了。

要注意的是 statement 內的引號 " 要去掉,format string 通通使用 %s,像下面兩個語法就會有問題:
cursor.execute('SELECT * FROM `user` WHERE username="%s";', username)
cursor.execute('SELECT * FROM `user` WHERE id=%d;', my_id)

另外,也不要把 format string 串起來才傳過去,這樣就會真的發生 SQL injection了:
cursor.execute('SELECT * FROM `user` WHERE user=%s AND password=%s;' % (usr, pwd))

至於其他的 database 可以找看看有沒有 prepared statement 可以使用。


參考資料

Python: 頑固的 variable length argument

這邊卡了很久,做個筆記...

可變長度的參數傳遞
def p(fmt, *arg):
    print fmt % arg

# 傳多個參數
s1 = "hello"
s2 = "world"
p("%s %s", s1, s2)

# 把 list 的 element 當作參數
l = ["Allen", "Billy", "Elmer"]
p("%s %s %s", *tuple(l))

# 混合模式
l = ["Allen", "Billy", "Elmer"]
s = "hello"
p("%s, %s %s %s", *((s, ) + tuple(l)))
Output:
hello world
Allen Billy Elmer
hello, Allen Billy Elmer

2012/06/07

社交網路設計:在地圖中找尋附近的人

很多社交網路App都會用到地圖,以自己為中心,找尋附近的店家、朋友等等。
用程式的邏輯來表達就是:
以 (x, y) 座標為中心,找尋方圓 n 公里以內的點

資料庫不外乎就是儲存經緯度 (longitude, latitude)
-----------------------------
 id | lat       | lng
----+-----------+------------
 1  | 25.151000 | 121.549000
 2  | 25.010000 | 121.574000
 3  | 25.070000 | 121.589000


今天我想找 User1 附近 10km 以內的人
方法一
來看"距離"跟"經緯度"的關係是什麼,從 Decimal degrees - Wikipedia 知道,其實他們並非線性的關係,而是一堆三角函數算出來的,因為隨著緯度越高,單位經緯度所對應的距離就越短。從 別搗蛋 歸納出的對應表:
台灣地區:
兩地經緯度相差 0.5度:距離相差約 50公里
兩地經緯度相差 0.1度:距離相差約 10公里
兩地經緯度相差 0.05度:距離相差約 5公里
兩地經緯度相差 0.01度:距離相差約 1公里

2012/06/01

Mac OS 內建抓圖功能 (Print Screen)

抓圖指令
把抓圖儲存到桌面 (Command 就是蘋果鍵"⌘")
  • Command-Shift-3: 抓全螢幕
  • Command-Shift-4: 可用滑鼠自己選取範圍
  • Command-Shift-4, 再按Space: 抓取視窗元件

和上面一樣,只是儲存到剪貼簿
  • Command-Control-Shift-3
  • Command-Control-Shift-4
  • Command-Control-Shift-4, 再按Space

參考資料
Taking Screenshots in Mac OS X - Mac Guides


改變抓圖儲存的位置
打開終端機,下達指令:
$ defaults write com.apple.screencapture location ~/Desktop/Snapshot
$ killall SystemUIServer

若要改回來原本位置:
$ defaults write com.apple.screencapture location ~/Desktop/
$ killall SystemUIServer

參考資料
Change the Screen Shot Save File Location in Mac OS X


改變抓圖的預設檔名
預設的名字是"螢幕抓圖 (datetime)"
$ defaults write com.apple.screencapture name "Snapshot"
$ killall SystemUIServer

參考資料
Change The File Name Of Screenshot In Mac OS X - YouTube

2012/05/31

MySQL INSERT 與 UPDATE 的一些特殊情況用法

最近剛好在設計 social network 的 database schema,遇到很多 special query 的需求,在此一一記錄下來。

如果不存在才 INSERT,存在就 skip
舉例來說,如果想記錄某 user 是否去過某國家
-----------------
 user  | country
-------+---------
 tony  | US
 tony  | HK
 kelly | US
Query:
INSERT INTO `trip` (`user`, `country`) 
SELECT 'tony', 'US' FROM DUAL
WHERE NOT EXISTS (
  SELECT 1 FROM `trip` WHERE `user` = "tony" AND `country` = "US" LIMIT 1
);
* 想 INSERT 的 table 可以跟檢查存在性的 table 不同個。

參考資料
mysql - Insert Where Not Exists-Without Primary Key - Stack Overflow



如果不存在才 UPDATE,否則 skip
舉例來說,你想讓 user 更新 email 欄位(unique),但 email 又不能跟其他人一樣:
------------------------------
 user      | email
-----------+------------------
 olivia    | olivia@email.com
 andy_wang | andy@email.com
 andy_lin  | (NULL)
Query:
UPDATE `profile` SET `email` = "andy@email.com" WHERE `user` = "andy_lin" AND NOT EXISTS
(SELECT 1 FROM `profile` WHERE `email` = "andy@email.com");
但你會發現 mysql execute query 會有下面錯誤:
You can't specify target table 'profile' for update in FROM clause
應該改成:
UPDATE `profile` SET `email` = "andy@email.com" 
WHERE `user` = "andy_lin" AND NOT EXISTS
  (SELECT * FROM (SELECT 1 FROM `profile` WHERE `email` = "andy@email.com") temp);

參考資料
MySQL can’t specify target table for update in FROM clause | VerySimple