2018年6月18日 星期一

文字雲 word cloud in python

這篇也是因為工作上需要,所以用Python進行文字雲的分析,想要從常用字詞,進行資料的分類,預期的結果除了統計常用字詞的次數以外,還有好像很炫但要細心看的文字雲圖式,這次做的是【以五月天30首熱門歌曲文字分析,統計出常用字詞,並以五月天 Mayday 的logo視覺化】 (如有侵權請告知,我很怕被吉...)


插個題外話,寫著寫這(其實是貼著貼著),覺得應該寫一系列叫做【輕輕鬆鬆複製貼上 寫Python】,Keep Calm and Copy Paste Series#2 (Keep calm 系列的典故請看這,看個blog順便學學歷史,做個好深宅)

圖片來源: The Keep Calm-o-matic

回到主題,如果要文字分析產生文字雲,有很多工具或網站可以直接使用,但如果想要用自己的字典跟特殊的停用字句,還是需要自己動手做。既然Python也不難寫,就來動手做一下,以後也好累積自己的字典跟停用詞句,以後也方便自己做分析分類。
這次是用五月天 在KKBOX 2018/6 最流行的30首歌詞分析,目的是知道哪些字詞是常用的語句,在應用上滿多種的,如果是分析PTT,那就等於是特定主題的輿情分析,其他領域可以是文章分類等非結構化的文章判斷,那就來看一看程式跟結果吧:

    • Step 1 Import 與定義變數


# coding: utf-8
# 部分參考: https://ithelp.ithome.com.tw/articles/10192043 
#       非常感謝王選仲大大
# 部分參考 http://blog.fukuball.com/ru-he-shi-yong-jieba-jie-ba-zhong-wen-fen-ci-cheng-shi/
#       非禪感謝林志傑 Fukuball大大
# 字典來自 https://git.oschina.net/fxsjy/jieba
#       真的是做最好的中文分词组件
#

from PIL import Image
import matplotlib.pyplot as plt
from wordcloud import WordCloud, ImageColorGenerator
import jieba
import numpy as np
from collections import Counter

    • Step 2 利用Jieba切字
#要分析的來源,這個範例是五月天30首歌
text_from_file_with_apath = open(r'lyrics.txt', "r",encoding="utf-8").read()

#設定字典
jieba.set_dictionary('dict.txt.big')
#設定自訂的字典,譬如五月天的 "動次"
jieba.load_userdict(r'userdict_mayday.txt')

#設定停用詞,譬如唱歌會用到的oh,喔
with open(r'stopWord_mayday.txt', 'r', encoding='utf8') as f:  
    stops = f.read().split('\n') 

#
#開始段詞與排序,沒錯就這麼簡單就做完了
#
terms = [t for t in jieba.cut(text_from_file_with_apath, cut_all=True) if t not in stops]
sorted(Counter(terms).items(), key=lambda x:x[1], reverse=True) 

    • Step 3 分析完後丟給文字雲繪圖
#中文繪圖需要中文字體,請自己從windows font目錄抓
#微軟正黑體
font = r'msjh.ttc'
#想要文字雲出現的圖示
mask = np.array(Image.open(r"mayday_mask.png"))

#背景顏色預設黑色,改為白色
#mark改用五月天的皇冠
#其他參數請自行參考wordcloud
my_wordcloud = WordCloud(background_color="white",mask=mask,font_path=font,collocations=False, width=2400, height=2400, margin=2)  
my_wordcloud.generate_from_frequencies(frequencies=Counter(terms))

#產生圖片
plt.figure( figsize=(20,10), facecolor='k')
plt.imshow(my_wordcloud,interpolation='bilinear')
plt.axis("off")
plt.tight_layout(pad=0)
#顯示用
plt.show()

#存檔用
#plt.savefig("Mayday_Wordcloud.png")



mayday_mask是五月天的皇冠logo,當作遮罩,套上文字雲後,最後的結果就類似這樣,

看來 "一天"、"快樂"、"相信"、"人生"、"回憶"、"愛"是五月天常用的文字。

如果朋友們有更簡單的方式也歡迎跟小弟交流教學相長~~
如果文字內容或圖片有侵權也請告知

#Python
#資料分析
#Word cloud
#文字雲
#Sky is limit

2018年5月26日 星期六

網路爬蟲開發 - 氣象局自動雨量站-雨量觀測 in Python



  • 前言

近期因為工作上需要氣象局的雨量資料,所以開始找相關的介接程式,從政府資料開放平台 (https://data.gov.tw/) 上面共有2個可以介接的資料來源

  1. 氣象局API: https://data.gov.tw/dataset/9177 
  2. 環保署Opendata: https://data.gov.tw/dataset/7879 

實際上來源都是氣象局,但環保署Opendata的資料集已經整理過是平面的資料表,直接用python pandas 可以直接 read_csv or read_json方式直接讀取,但也許是介接的人太多,效能很差,常常要等很久且抓不到資料,如果要作認真的應用(真的要拿來每小時分析或計算)會很困難,因此這一篇是以氣象局的API方式介接並寫入資料庫。


  • 介接方式
透過氣象局Open Data API取得資料,資料經過整理後,再寫入MS SQL Server

  • 前提
必須在氣象局 Open Data 網站 (http://opendata.cwb.gov.tw) 註冊帳號並取得授權碼,如果有很多資料集的應用,需要詳細研讀解氣象局呼叫方式。

    • Step 1 Import 與定義變數
# coding: utf-8

# 
# Purpose: 自中華民國氣象局 Open Data 取得自動雨量站 每10分鐘雨量資料 
# Remark: 解析json資料有參考 https://github.com/leafwind/cwb-cache 非常感謝
#
import logging
import pandas as pd
import requests
from datetime import datetime
import sqlalchemy

#氣象局API網址
CWB_URL = 'http://opendata.cwb.gov.tw/api/v1/rest/datastore/'
#自動雨量站 資料集代號
DATA_ID = 'O-A0002-001'
#氣象局 OPEN DATA 授權碼,需要自行修改
AUTH_KEY = 'CWB-OOOOOOOO-OOOO-OOOO-OOOO-OOOOOOOOOOOO'
#目的地 SQL Server 的連結字串,需要自行修改
SQL_CONNECTION_STRING = "mssql+pyodbc://[account]:[password]@[SqlServer]:1433/[Database]?driver=SQL+Server+Native+Client+11.0"
#目的地 SQL Server Table名稱,需要自行修改
DEST_TABLE = 'tb_Rainfall_API'
    • Step 2 取得API的資料集
這段用function方式模組化程式,感謝 leafwind的大作 https://github.com/leafwind/cwb-cache

def get_data_from_cwb(data_id, auth_key, params={}):
   '''limit, offset, format, locationName, elementName, sort'''
   logging.info('getting data from CWB...')

   dest_url = CWB_URL + '{}'.format(data_id)
   r = requests.get(dest_url, headers={'Authorization': auth_key})
   params_list = ['{}={}'.format(key, params[key]) for key in params]
   params_str = '?' + '&'.join(params_list)
   dest_url += params_str
   logging.debug('dest_url: {}'.format(dest_url))
   
   if r.status_code != 200:
       logging.error('r.status_code: {}'.format(r.status_code))
       return None

   data = r.json()
   
   if data.get('success') != 'true':
       return None
   return data
    • Step 3 解析API資料成為Dataframe
雖然概念上很簡單,但是因為解析那段有點麻煩,所以沒辦法很直覺的爬回來就準備寫入資料庫,所以需要轉換,可以直接呼叫這個網址當範例 (只查兩站,速度比較快),我們需要的資料是藍色框框,records\location 向下的資料,其中 weatherElement 是雨量的值。呼叫的function 如下。


def parse_json_to_dataframe(data):
   columns = ['stationId','locationName','lat','lon', 'obstime','ELEV','RAIN','MIN_10','HOUR_3','HOUR_6','HOUR_12','HOUR_24','NOW']
   df = pd.DataFrame(columns=columns)
   dataDic = {}
   locations = data['records']['location']
   row = -1
   for l in locations:
       row = row + 1
       dataDic['stationId'] = l['stationId']
       dataDic['locationName'] = l['locationName']
       dataDic['obstime'] = l['time']['obsTime']
       dataDic['lat'] = l['lat']
       dataDic['lon'] = l['lon']
       factors = l['weatherElement']
       for f in factors:
           factor_name = f['elementName']
           dataDic[factor_name] = f['elementValue']
       for key in dataDic.keys():
           df.loc[row,key] = dataDic[key]
       

   return df 


    • Step4 主程式:取得資料、解析成為資料表的格式、寫入資料庫
if __name__ == '__main__':
    json_data = get_data_from_cwb(DATA_ID, AUTH_KEY, {})
    df = parse_json_to_dataframe(json_data)
    df['InsertDatetime']=datetime.now()

# In[9]:

#寫入資料庫

engine = sqlalchemy.create_engine(SQL_CONNECTION_STRING)
conn = engine.connect()

df.to_sql(DEST_TABLE,engine,if_exists='append')

#確認資料庫結果
rs = conn.execute('SELECT TOP 10 * FROM ' + DEST_TABLE +' with (nolock) ORDER BY InsertDatetime desc ')
_result = pd.DataFrame(rs.fetchall())
_result.columns = rs.keys()
conn.close()

_result


  • Step5 進入資料庫的資料處理
對於這個資料集,會有一些額外的資料面要處理,如雨量的監測值-998轉成0等等,小弟認為這些是資料庫端處理會比較方便,譬如寫一個 stored procedure 或 trigger處理資料,這些就給各位依照自己的需求再調整了。



進入資料庫後,就容易進行後續的應用、計算與分析了!
如果朋友們有更簡單的方式也歡迎跟小弟交流切磋~~

#Python
#Crawler
#氣象局
#環保署
#雨量
#Sky is limit

2017年11月20日 星期一

網路爬蟲開發 - AQIStudy Crawler

因為近期有需要去中國網站抓空品資料分析的需求,他是一個城市一個城市個別放的https://www.aqistudy.cn/historydata/

備註 AQI 是計算空氣品質指標的方式,詳細請看我國環保署的網站

為了比較方便處理,因此通通抓到資料庫再用Query或丟到Excel處理。處理方式是利用python去爬爬爬,放到MS SQL,再用SQL的方式進行分析比對,任務很簡單,但處理起來需要一些技巧。

1. 抓資料

#import需要的工具
import pandas as pd
import sqlite3
import urllib.parse
import sqlalchemy, pyodbc
from datetime import datetime
from selenium import webdriver

#取得所有城市,程式清單請自己整理成TXT
def get_city_set():
    str_file = r'city.txt'
    fp = open(str_file,'rb')
    city_set = list()
    for line in fp.readlines():
        city_set.append(str(line.strip(),encoding='utf-8'))
    return city_set
city_set = get_city_set()
#開始爬資料
base_url = 'https://www.aqistudy.cn/historydata/monthdata.php?city='
#SQLServer的帳密,請自己改
engine = sqlalchemy.create_engine("mssql+pyodbc://[帳號]:[密碼]@[主機]:1433/[資料庫名]?driver=SQL+Server+Native+Client+11.0")
conn = engine.connect()

for city in city_set:
    weburl = ('%s%s' % (base_url,urllib.parse.quote(city.encode('utf-8'))) )
    print(weburl)
    driver = webdriver.PhantomJS()
    driver.get(weburl)
    dfs = pd.read_html(driver.page_source,header=0)[0]
    driver.quit()
    dfs['City']=city
    dfs['InsertDatetime']=datetime.now()
    print(city,"\t",len(dfs))
    dfs.to_sql("[表格名]",engine,if_exists='append')

conn.close()
#判斷結果
engine = sqlalchemy.create_engine("mssql+pyodbc://[帳號]:[密碼]@[主機]:1433/[資料庫名]?driver=SQL+Server+Native+Client+11.0")
conn = engine.connect() rs = conn.execute('SELECT [City],count(*) FROM tbAQI with (nolock) group by [City]') for row in rs: print(row) conn.close()

2. 分析
 用Pivot就可以方便資料分析人員在Excel做逐時的比對

select * from 
(
 select city,'PM25' as [Object],[月份],[pm2.5] from [表格名]
 group by city,[月份],[pm2.5]
)as S
PIVOT
(
 sum([pm2.5])
 for mon in ([2014-01],[2014-02],[2014-03],[2014-04],[2014-05],[2014-06],[2014-07],[2014-08],[2014-09],[2014-10],[2014-11],[2014-12],
  [2015-01],[2015-02],[2015-03],[2015-04],[2015-05],[2015-06],[2015-07],[2015-08],[2015-09],[2015-10],[2015-11],[2015-12],
  [2016-01],[2016-02],[2016-03],[2016-04],[2016-05],[2016-06],[2016-07],[2016-08],[2016-09],[2016-10],[2016-11],[2016-12],
  [2017-01],[2017-02],[2017-03],[2017-04],[2017-05],[2017-06],[2017-07],[2017-08],[2017-09],[2017-10])
)AS P

貼到Excel就長這樣,可以多城市一起比對分析,方便!


#Python
#Crawler
#AQI
#AQIStudy
#Sky is limit

2014年1月31日 星期五

Agile Contract Type (III) Comparison

在前兩篇整理了幾種合約類型,最後一篇是整理比較表。對於合約,就像「好吃」的定義一樣,每個人都可以講一套自己的理論,所以小弟找了一個甲方資深同學,請他加上甲方對各類合約的想法,還好這篇在 Blog 發表,不是論文,要不然就只好叫他對自己的內容 "出來面對" ╮(╯_╰)╭。除了甲方的想法外,加上自己的一些想法,合約的目的是促進甲乙方的合作,對於可能發生的風險可以有一個雙方都可以接受的方案,而不是造成甲乙雙方互不信任,各位捧有,Agile Manifesto 講的 "與客戶合作重於合約協商 Customer Collaboration over Contract Negotiation"  說的真是有道理阿~~

合約類型 小弟 Comment 資深甲方's Comment
固定價格合約(Fixed price) 範圍夠明確的專案才比較適合。
現在台灣的軟體專案大部分都簽這種類型的,但是台灣的甲方(特別是私人企業端的)對「完成」的定義會無限上綱,一不小心就是相看兩厭..甲方覺得乙方難配合,乙方寧願不收驗收期的款項,儘早開始往下一個乙方專案。
至於固定價格合約(Fixed price) 我的心得 就是合約階段 一定要跟老闆想要的階段 整合在一起(老闆想要的就是我們這種小小承辦人績效來源)
有一次有一個一年大案子 基本上分 三個階段 要完成 但第一階段 佔50%工作量 卻被要求 2個月內要完工 意思就是合理工期至少6 個月的 工期 卻被要求縮短30% 完工, 我只好使出殺手鐧: 我跟廠商說你幫我做到 我一次直接先給你2個階段費用 ) 反正後面還有第三階段 二三階段 廠商看在第三階段款 也不太可能不做。那一次經驗 果然廠商在我 [威脅利誘]下 又早2天完成第一階段上線…。
時間與材料合約/有上限的時間與材料合約( T&M / T&M with cap) 適用一定範圍內的解決問題
解決個Bug、改個什麼確定的程式功能,這種就滿適合的。看甲方是時間優先還是預算優先,高手貴一點,資淺的便宜一點,有時候解決問題比錢重要...
至於時間與材料合約/有上限的時間與材料合約( T&M / T&M with cap)跟固定價格合約(Fixed price)  我通常運用在 不同性致的廠商或專案上:

如果這個需求非常急 就是那種 7/25 跟你說 8/1 一定要上,如果不上公司會遭受多少莫大損失,而手上廠商對需求範圍熟悉度很低,根本時間不讓我做到最簡單的需求確認 /SA 分析/SD分析 /CODING 測試 ….我就會直接選擇時間與材料合約/有上限的時間與材料合約( T&M / T&M with cap) 直接跟對方談 我跟你買5天人CODING 人力 你派人來 我直接跟你說改哪邊 做什麼?(用嘴巴作完需求確認 /SA 分析/SD分析….)  基本上我還會多利用目標成本合約精神,我會跟廠商說 我會至少買5 天 ,三天你的人就做完 那是你的本事 不會少給你2 天。 那一次廠商 就派個高手來 4HR 作完5 天要做的事情!!!!
漸進式交付合約(Incremental Delivery) 長時間、大範圍的專案應該都考慮用這種專案
事實上大範圍應該先切分階段個別簽約,對雙方都會比較好,先有一個小專案磨合比較容易瞭解,但是有時候就一定要一次發一大包的話,那應該切多個小階段,每個階段做完考慮還需不需要繼續。如果只為了消化預算而繼續開發價值低(使用頻率低 或是 為了未來一兩年後可能發生需求..)的系統,那才真的是浪費錢...
 一般軟體外包客製和約,買方公司不太可能簽漸進式交付合約(Incremental Delivery 因為如果這個需求 至少要買5箱蘋果 你不可能跟老闆說 我們先跟對方簽 10買10箱蘋果  然後再看看要用到幾箱 因為老闆絕對會質疑承辦人 阿 你是不是 搞不清楚需求 不然 幹嘛抓不準?(承辦人寧願買貴 不願買錯…買錯是要負政治責任!!!!)
目標成本合約(Target Cost Contract) 如果甲乙雙方已經建立信任的基礎,對於軟體專案,應該多多考慮採用這種合約,激勵甲方也讓乙方提早使用到有價值的軟體。 目標成本合約(Target Cost Contract) 也不太可能 同樣道理 如果只要花 100 元就可以買到 5 箱蘋果 老闆不會允許 你 如果提前一個月收到蘋果 少付對方 20元,老闆只會要求: 你就是給我提前一個月辦完事情 而且我只願出80 元 ,(小弟 曾經有一次還遇到 老闆不只80 不願出 只願給 50 元  因為 老闆的朋友跟他說 你買貴了…直接硬砍50%)



總結小弟甲乙方都做過,個人觀察,這幾年,台灣的甲方因為陸續導入ERP,被外商訓練過,現在對軟體專案的進行方式都比較熟悉,不會像以前一樣只用價低者得標、又愛凹、結案又愛挑東挑西不給結案,現在比較能接受不同方式。
如果看官你是甲方承辦人,小弟建議你可以依照不同的專案內容,選擇適合的合約方式;
如果看官你是乙方,為了專案執行順利,請多想想可以用什麼合約方式可以讓乙方可以在對的時間獲得價值!
說來說去好像還是在說錢,錢在採購合約上絕對是關鍵,但絕對不是最大關鍵, 比如我們公司所有承辦人就很討厭跟大軟體公司合作 尤其那種在台成立20幾年的…..因為1他不缺錢 2.溝通都派業務 (業務都直接找老闆搞定我們這種小承辦員) 業務都年輕貌美說話甜 3. 作事太沒彈性,沒彈性出事時等於逼承辦人什麼都自己扛,等於逼承辦人去跳淡水河…..。4.如果真不幸,大公司還養打官司法務,如果案子搞到打官司 那承辦人 早就跳N次淡水河。

實務上其實很難 一招打片天下無敵手,不同CASE要運用的手法往往不同,良好的買賣關係,其實就跟人際關係一樣要有點黏又不能太黏, 而有些關係還得花時間花力氣去培養,比如如果配合還不錯公司,我會選擇犧牲一時換得長久信任(出小事要幫廠商扛 不要什麼事都要廠商吃下來 總一天 他會要你整個吞回去…….)。



各位看官如果有你自己一套的採購原則或是經驗、案例的分享,也歡迎留言或寄信給我。累積越多合約案例


小弟的敏捷式專案的採購領域整理到這邊,算是已經解答了自己的疑問,再來的主題,是怎麼樣做都不對的績效考評,在敏捷專案領域又怎麼看..











2013年7月29日 星期一

Agile Contract Type (II) - Incremental and Target Cost

接續著上一篇的主題,這一篇則整理另外 2種Agile合約,在國外的討論或是書上會看到的,這兩種在台灣就不是這麼常聽到了:
  • 漸進式交付合約(Incremental Delivery) (1)(2):這種合約會設置好幾個檢查點,每個檢查點可以讓甲方決定還要不要繼續下去,不繼續的話,已經完成的功能也是能運作的軟體,而不是一堆做到一半的程式碼。這種方式是比較適合Agile Project Management,因為每個 Iteration 交出的成品都是可運作的軟體。譬如一年的專案,可以在每一季設一個檢查點,如果乙方的Agile Team每個 Iteration 是 2 週,每個 Iteration 都交付一些重要性高的、可以用的軟體功能,6個 Iteration 時(也就是3個月),甲方就可以決定還要不要繼續下去,如果想要更經常的檢視完成品,也可以雙方討論後,修改檢查點的時間。
  • 目標成本合約(Target Cost Contract) (3 對細節有興趣的朋友,可以花點時間看):這種合約是 Toyota 跟供應商長期簽訂的合約類型,上課的時候也是講說 Agile Project Management 建議簽這樣的合約類型。這種合約先由甲乙雙方決定一個目標金額,專案開始後,則依照比例甲方付款給乙方,而如果乙方提前完成目標,則兩方共享節省的金額,而如果超過目標金額,則兩方共同承擔懲罰。而獎賞或懲罰的比例則事先訂定下來,如果想拿大比例的獎賞,那就要承擔大比例的懲罰。這種合約類型用圖解釋比較清楚:


    • 專案的基本數字:
      • 專案總金額:500 萬,額外準備100萬,總專案的最高費用(Cap)是600萬
      • 簽約時的付款:不先付任何款
      • 結案的付款:結案時給50萬
      • 每月工資:50萬
      • 預計工時:10個月
    • 專案 3種狀況(正常完成、提前、延誤)的付款進度如圖:



    • 正常進度:紅線,每月固定50萬,最後第10個月後到500萬,沒有獎勵也沒有懲罰。
    • 提前:綠色的線,假設提前在第8個月做完的話,可以領到
      • 8個月的400萬
      • 結案時可以領的50萬
      • 激勵金額 (目標的500萬-完工400萬)*50%=50萬,全部加起來也是500萬。在這個情況下,甲方可以提早開始使用系統帶來的效益,而乙方可以早領錢、早結案,專案人員可以慶祝(在台灣軟體開發專案能夠提前,真是難得啊...)並準備投入下一個專案。
    • 延後:在到 10th個月後,11th個月起,甲乙方各付一半(因為獎金也是甲乙各一半),所以11th個月起,每月只能算25萬,一直到550萬為止,所以到12th月後,累計就已經550萬,加上結案可以收到的50萬,就已經到頂(600萬)了,這時候乙方就必須不領錢直到做完為止,雖然看起來只有乙方在suffer,但是實際上甲方一直沒系統可用,這樣造成的損失也是甲方要承擔的懲罰。
    • 因為Target Cost Contract 比較特殊,可能有一些實行上的細節要考慮,除了看 (3) 以外,也可以看這篇 Agile Journal 2005年的 Selling agile: target-cost contracts ,講到非常實務上的細節。
小弟在整理這些合約類型的過程中,如果是乙方,可能需要先取得甲方的信任,在第2次、第3次的專案中,再開始用新的合約類型,比較不會引起甲方的過度懷疑,小弟認為做專案就是信任大考驗,在雙方互信的狀況下,怎麼樣都好談,要是雙方無信任,那就很難再繼續下去....
下一篇則要來個整理表了,比較各專案類型的適用對象,與陰謀論的想一下甲乙方會承擔的風險。

參考

2013年7月27日 星期六

Agile Contract Type (I) - Fixed Price and T&M


這個圖是你的合約關係嗎?

在上Agile Project Management的課程時,一直想跟實際工作情形連起來,連到採購的時候,心中有的大問號:現在一般常見的系統開發合約(開發一個系統,分3或4期付款:簽約、系統設計完成、開發完成、結案) ,跟以 Iteration 為進行方式的Agile Project,要怎麼對應的起來? 用Agile 開發的話,不把全部的SA/SD文件都寫完啊,那要怎麼付款跟請款 ?
上完課後,為了一解心中的疑惑,開始蒐集 Agile Contract 的類型,這一系列分3篇,算是對整理後的筆記,先整理目前常見的合約類型共4種,最後再加上一個比較表,說明適用的時機與缺點。

這一篇先講兩種常見的固定價格合約(Fixed Price and Fixed Scope)與工時與材料(Time and Material)類型的合約:
  • 固定價格合約(Fixed price):是目前最常見的軟體專案發包方式,這種合約通常有固定的總金額與專案範圍,通常付款是分為:
    • (1)合約簽約付一筆
    • (2)中間階段0~N次付款:通常可以分 "系統分析文件交付"、"系統設計文件交付"、"系統開發完成交付"、"系統通過使用者驗收測試(UAT)" 等等不同階段
    • (3)專案結案付一筆
    • 譬如一個中型專案,乙方為了公司營運金流的順暢,希望分 4 次付款,所以請款的時候會是 "合約簽約"、"系統分析、系統設計文件交付與確認"、"通過UAT"、"專案結案",這樣的合約配合瀑布式開發方式是很合適的,各階段都能搭配上。
    • 在Agile Project Management時,並不會一開始完整的分析整個系統,用 固定價格的合約 就會覺得有點卡卡,譬如甲方要求 "SA, SD文件確認" 付款時,乙方不知道要怎麼產出 超大本的SA/SD文件 去給甲方確認,所以出現其他比較符合Agile的合約類型。但是,如果甲方說,只能發包這樣的合約(組織要求或...),乙方是可以應用一些觀念,讓合約比較能符合Agile開發方式,譬如偷偷引入漸進式交付合約的想法(Incremental Delivery),把Release當作階段 而不把SA/SD文件完成當作階段 (完成子系統#1,2當作第一階段、完成子系統#3,4,5 當作第二階段)。
    • Scrum 大師Jeff Sutherland 提出的 2個重要的合約的觀念 (1) ,如果必須要承接/發包固定價格的合約,可以加入到合約中,對甲乙雙方都會有幫助的 (小弟不喜歡用"雙贏", 講出雙贏的人通常都不會說自己贏比較多...)
    • Money for nothing 金錢無用 :甲方可以在任一個 Iteration 後結束專案。評量的標準是, 如果甲方認為後續要做的價值不如已經完成的價值(i.e 已經完成的功能已經能達到所需要,因為Agile 是從高優先順序開始開發) 甲方將會付給乙方剩下合約的20%的金額。( 金錢無用 -> 不想付錢買不會用到的功能..) 
    • Change for free 改變免費:雖然講免費,但實際是,當有改變的時候,Product Owner 在 Iteration Planning 前或中提出新需求,Agile Team 評估 Story Point,確定要做的話,Product Owner把不重要的功能或相等Story Point的功能的移出 Product Backlog,做一些 TradeOff,甲方的高優先需求得以滿足,乙方一樣做那麼多Story Point 的工作,以這個方式來歡迎改變!

  • 時間與材料合約/有上限的時間與材料合約( T&M / T&M with cap):跟台灣俗稱的的"買人力"的想法很接近,可以想成"買整組人"在甲方駐點(通常還是要駐一下, out of sight out of mind, XD )開發所需要的系統,這種方式就是做多久就付多少金額。因為時間都是乙方估計,所以風險都在甲方,也因此有變種的,是加上上限的T&M(Capped T&M)合約,這個上限是雙方同意後的金額,通常甲方應該要加上鼓勵條款,鼓勵乙方早日完成。(不知為何,在台灣的甲方,能夠準時驗收與準時付款,對乙方就是一種激勵...) (2)

下一篇將整理的是漸進式的與目標成本的合約。

參考

2013年7月13日 星期六

[Engineering Practice] 軟體開發的測試-單元測試 / Software Development Testing - Unit Testing

以前只要講到系統功能要測試,通常就會想到找 PM、SA或直接給客戶進行人體測試,結果就會發生一使用功能就 Error 的悲劇... 。今年去上 Agile Project Management 時, 講到強調軟體開發的Extreme Programming時,特別強調測試,在 "XP 規則" ( The Rules of Extreme Programming原文 )中,有一組規則是講測試的,貼上來給大家震撼一下:
  • All code must have unit tests. 所有程式碼都要有單元測試 !!!!!
  • All code must pass all unit tests before it  can be released. 所有程式碼通過單元測試後才能release !
  • When a bug is found tests are created. 如果有發現 bug 時,就要回去寫測試 !!!
  • Acceptance tests are run often and the score is published.  經常執行驗收測試,並且必須公布量化指標!!
對XP Unit Test 有興趣的朋友,可以再看這一篇: Unit Test ..

既然單元測試是這麼重要,在上完Agile的課後,小弟開始學習在.Net Framework 要怎麼做單元測試,這一篇算是介紹,也是對自己學習的筆記,我使用的工具是 C# 與 Visual Studio 2012,使用 Java 的朋友,你一定能找到對應的工具與平台,這年頭, M$ 會推出某個功能,都是被 Java Community 逼出來的..ㄎㄎ... 廢話不多說,我用問題與回答來說明單元測試:

1. 單元測試到底是指甚麼?  =>預期結果等於測試結果
  • 簡單說,單元測試是對程式的最基本的單位進行測試 ( 最基本單位是要看你自己認定,小弟是非常程序導向的開發人員 i.e. old ,我的通常是 Function ),確認程式能夠如預期的執行。小弟的心得是,因為要單元測試,會強迫自己開始規劃自己的程式,讓 function 真正能比較像一個 function 該做的事,不會通通塞到一起才來拼命 debug line by line (還記得以前老師教的高聚合低耦合嗎?... )
  • 在單元測試會做的三件事:這跟人體測試時的邏輯是滿相同的,假設你要呼叫一個兩數相除的 function,會有3 步驟
    • Step (1) 先準備呼叫的資料,如 100除以5,所以你會把100跟5先準備好,這個步驟就是 "安排測試資料(Arrange)"
    • Step (2) 開始呼叫Function,並取得結果,譬如這個function可正常呼叫並回傳20,這個步驟就是 "執行測試(Act)"
    • Step (3) "判斷結果(Assert)其實在提供資料去測試時,你會有預期的結果,所以從第2步取得結果後,就會跟你預期的結果進行比對,以判斷兩數相除的Function是否如果預期執行,譬如 100/5=20,function回傳結果也是20,所以你就可以判斷function是否運作正常了!
    • 下圖是我寫測試一個上傳檔案的 Function 是否正確的單元測試程式,其實跟真正要呼叫該 Function 的程式會很類似,所以其實單元測試寫完,就可以copy/paste到正是用的程式,一魚兩吃阿!


2. 要如何開始做單元測試 ? =>打開你的好朋友(?!) Visual Studio
  • 這個問題跟使用的程式語言與開發工具非常相關了,我用Visual Studio 2012來當範例,基本操作在網路上已經很多教學怎麼做了 (如果跟我一樣從未接觸過,可以參考這一篇 MSDN,照著做一次會有感覺...) ,步驟大致是:
    • (1)在你目前開發的"方案"中,加入一個 "單元測試專案"
    • (2)在新的單元測試專案中,增加"參考" 開發的專案
    • (3)在新的單元測試專案中,開始寫Arrange, Act, Assert的程式。 請注意的是,在一個單元測試中,可以放多筆測試資料,預設是只要一個測試未通過,就會當作不成功,可以多放幾筆確定 function 真的跟預期的結果相同 (該有回傳就回傳,該有Exception 就會有 Exception...)
  • 寫完後,可以直接按 "測試總管" 的 "全部執行",就會知道是否通過單元測試了! 在測試的過程中,再回去改程式,我覺得這個過程是最大的收穫 !

3. 要如何確認自己做夠多的單元測試? => 計算程式碼涵蓋範圍
  • 還記得剛才的XP Test Rule "所有程式碼都要有單元測試 !",所以要如何判斷自己有涵蓋夠多的程式? 你可以使用Visual Studio 2012的  "分析程式碼涵蓋範圍"功能 ,馬上就可以看到涵蓋結果。至於你的目標要設為多少,可能要由你的 developer team 自行在definition of done 中定義。 
  • 如果你有引用Web Service 或 WCF,預設在計算程式碼涵蓋範圍時,會把這些外部參考也會當作範圍,這樣分母就會很大,涵蓋率就會變低,看了就不爽! 這時你可以自己加上一個設定檔(參考這一篇 Customizing Code Coverage Analysis ,最下方有範例檔案),並且在裡面增加排除Web Service/WCF,這樣就不會把外部的算進去了! 想要直接下載的可以到這邊下載,加到你的測試專案中,並在 "測試"->"測試設定"->"選取測試執行檔"使用這設定。
    • 加入後設定檔案後,要自己指定


    • 設定檔案中,如果不想納入Web Service/WCF,這邊要加上排外的設定。設定完再執行一次 "分析程式碼涵蓋範圍" 就會看到變化囉 !



4. 要如何準備單元測試的多筆測試資料? => 資料驅動的測試方式
  • 如果你的developer team有專職的測試人員或是你的Product Owner/Customer 願意準備測試資料,這時你就可以使用 資料驅動的測試方式 (Data-driven test) 。這方式可以用 CSV, EXCEL, Database 當作資料來源,而在單元測試時,就可以取得這些資料,進行多次的測試,太棒啦,測試可也切成資料與邏輯 !  
  • 使用 data-driven 測試有幾個眉角需要注意的
    • (1)資料上,準備時要有輸入的參數,還有預期的結果,譬如大家都熟悉的 Excel ,我有一個function要驗證帳號密碼是否正確,所以有3個欄位:帳號,密碼,預期結果,類似下圖

    • (2)資料上,檔案要自己加入倒測試專案中,並且在 "複製到輸出目錄" 要選 "永遠複製"
    • (3)程式上,要自己加上 using System.Data 
    • (4)程式上,要加上 private TestContext testContextInstance; 的一段
    • (5)程式上,要加上 Data Source,Connection String 可以參考這篇 Data Driven Testing with Visual Studio 2012 – Coded UI Test ,已經整理好了,改改檔名就可以。
    • 這段的程式是這樣的,其實很單純,但是  Visual Studio 2012 就是不會自己做.


5. 要如何把自己的 Function 跟別的 Interface/Library 隔離?  => Fake 機制
  • 有時候測試的時候會希望跟別的 Interface/Library 切割的比較清楚,譬如拿到輸入資料後不寫入資料庫,只要判斷程式邏輯正確,這時要怎麼切割呢? 在 Visual Studio 2012 時加上了 Fake 的機制,請一定要參考這一篇 VS 2012 的 Unit Test與測試總管 的"建立 Fake 組件"  與這一篇 Isolating Code Under Test with Microsoft Fakes ,Fake機制裡面還可以分為 Stub 與 Shim ,各有各的適用對象。以下小弟舉一個簡單的範例,使用的是Shim給大家參考:我要做的是,不去資料庫檢查帳密,我只要確定帳號密碼不一致就可以,所以我用Shim 隔離我的測試程式與實際的程式。


6. 要如何自動Build 自動單元測試?  => TFS 現在5人內免費!
  • 如果你是 one person team (獵人的磊扎,就是下圖那一位,1個人可以當5個人),那你可以不用考慮這個了 "持續整合/持續集成"這個議題,你自己應該會持續的 build,並且你的開發環境上就是 releasable build...

  • 如果你不是 one person team,一定會遇到版本控制、自動整合Build、自動測試的議題。在這部分,我是使用免費的tfs,現在是5人以內免費,適合small team (M$可能不知道台灣軟體廠商 >5個工程師的專案是很大的專案 哈...)。這一段的內容我是參考這一本書 軟體測試實戰 Visual Studio & Team Foundation Server (推! 雖然是visual studio 2010 ,但是還是很有參考價值,不知道會不會改版..) 書中的 "10-03 自動化組件與測試" 。使用方式大致是這樣:
    • (1) Check in 最新的程式版本到 tfs,Solution 應該也包含測試的專案!
    • (2)建立新的 組建定義,設定一下組建方式等等,譬如指定星期一到五中午12:30 tfs自己build自己測試...
    • (3)每天跑完後回去看結果,因為組建設定中,已經包含執行自動測試,所以在build完成後,tfs也會測試,測試的結果也在報告中,下圖是我前幾天的測試結果,可以看到單元測試錯誤! 



後記
  前前後後約花了約 1個月在Survey、熟悉這些工具的操作,並且在目前開發的系統加入單元測試。在開始加入單元測試後,發現了一些自己的盲點 (function執行的結果與預期不一樣),修改後也增加了程式的 robustness, Unit Test 好物!


參考資料