痛点(Pain Point)
那一天天气有点糟,心情也有点糟,雾霾持续多日。
在过去的五天里项目进度只有不到5%,而我们计划要在五天后达到25%,这样的进度无疑有很高的风险。项目进度不尽如人意,我是有一些焦虑和忐忑的。
这是一个测试项目,拥有68个用户场景(User Case),1300多个测试例(Test case)。美中印三地参与其中的共有二十多个工程师,分别属于四个团队,加之时差的关系,项目推进起来不是很轻松。
项目完成度在Sharepoint(微软出品,可在线协作)上使用在线excel 来追踪。每个工程师更新自己任务的完成量,汇总后得到整体的进度。
尽管有日历(calendar)通知,仍然会有一部分人忘了更新进度(或者进度确实落后)。这也不能怪工程师,人脑不是电脑,难免会健忘,尤其是工程师在同时处理多任务的时候。这是可以理解的,我也是如此。
成员少的时候可以逐一通知,无论是面对面,还是即时通讯软件,或者邮件单播都是有效的办法。可成员一旦多起来,加之跨不同时区,那就只能靠群发邮件了。但是群发邮件有个问题,由于邮件地址是组地址(alia),邮件内容只提所有人,并没有明确的指向,这导致了:第一,容易被湮没在茫茫邮海中,不是单播给我的邮件一定不重要,每个人都是这样想的。第二,有些已经完成目标任务的人也会收到通知,这是没有意义且浪费时间的。所以这种办法有如隔靴搔痒,效果差强人意。
难道就不能有个工具在适当的时候自动带着数据单播提醒一下这些记忆力不好的人吗?
这么多年了,没人管这事!——罗永浩
一起做事,不要认为自己负责的部分做完就可以了,想要把事情做好就要承担所有。
于是我写了这个提示进度的邮箱炸弹工具,起名叫PushNotification!
程序设计
流程很简单:先把excel表爬下来,分析数据,数据确认,发送邮件。
Sharepoint要做登录认证,不容易爬,这部分暂且手动下载。
编程语言使用Python,一方面是Python易用,另一方面是因为Python库多。比如读取excel表格,只需要安装xldr库就可轻松处理。
发送工作邮件是严肃的事情,发错内容或者乱发收件人可能会造成恶劣的后果,数据确认是一定要做的。输出内容也将作为记录,这样即便有错误,也方便追踪是哪里出了问题。
使用系统命令sendmail发送邮件,调用方法借鉴了stackoverflow的一个答案,代码注释中有链接可供参考。用Markdown制做了HTML格式的邮件模版,美观好看。
因为所有邮件不但发送给工程师,还要抄送其领导。这意味着,管理越多工程师的领导其收到的邮件也越多。即便邮件内容美观度有所提升,收一屏幕邮件也不是什么开心事。只有进度落后才会被提醒,所以能不能帮领导解脱,全靠工程师们对进度的追求了!:)
一共不到一百行代码,不用面向对象,面向过程,一个函数就好。
#!/usr/bin/env python
#coding=utf8
import re
import os
import sys, getopt
import xlrd ## for excel
## for sendmail
from subprocess import Popen, PIPE
from email.mime.text import MIMEText
def bomb_push (answer):
## initial data
sp_url='https://test.url.com'
mail_template="/xxx/push.mail.html"
## read excel and get data
data = xlrd.open_workbook('commitments.xlsx')
table = data.sheets()[0]
sheet = data.sheet_names()
ava_exe= table.row_values(76)[7]
ava_exe_per= format(ava_exe,'0.2%')
f=open(mail_template)
ff=f.read()
f.close()
for rownum in range(table.nrows):
#for rownum in range(33,34):
#print table.row_values(rownum)
uc_name = table.row_values(rownum)[2]
### python 过滤中文、英文标点特殊符号 http://blog.csdn.net/mach_learn/article/details/41744487
### efficiently replace bad characters http://stackoverflow.com/questions/6609895/efficiently-replace-bad-characters
uc_name = re.sub("[\xc2\xa0\s+\.\!\/_,\&$%^*(+\"\']+", " ",uc_name)
execution = table.row_values(rownum)[7]
owner = table.row_values(rownum)[12]
owner_mail = table.row_values(rownum)[12] + "@juniper.net"
manager = table.row_values(rownum)[13]
if manager=="":
manager="xxx@juniper.net"
else:
manager=manager+ "@juniper.net"
### skip invalued profile
if execution=="":
print "Skip No.%s %s for execution is empty" % (rownum,uc_name)
print "+------------------------------------------------------------------+"
continue
if table.row_values(rownum)[12]=="":
print "Skip No.%s %s for owner is empty" % (rownum,uc_name)
print "+------------------------------------------------------------------+"
continue
## compare execution
#if execution<ava_exe:
if execution<0.25:
if 'answer' in locals() and answer=="yes":
#print "execution is %s, average exe is %s" % (execution,ava_exe_per)
execution= format(execution,'0.2%')
### handle template
#print "EXE is ",execution
#print "AVE is ",ava_exe_per
#print "URL is ",sp_url
#print "UC is ",uc_name
#print "TO is ",owner
#print "ff is:\n ",ff
#print "+-----------------+"
#manager="rdguo@juniper.net" ## test
#owner_mail="rdguo@juniper.net" ## test
ff_mail=ff.format(EXE=execution,AVE=ava_exe_per,URL=sp_url,UC=uc_name,TO=owner)
### send email, refer to http://stackoverflow.com/questions/73781/sending-mail-via-sendmail-from-python
msg = MIMEText(ff_mail)
msg["From"] = "xxx@juniper.net"
msg["To"] = owner_mail
msg["CC"] = manager + ",xxx@juniper.net,xxx@juniper.net"
msg["Subject"] = "'%s' Was Executed %s" % (uc_name,execution)
p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE)
p.communicate(msg.as_string())
print "Done for No.%s %s %s" % (rownum,uc_name,owner)
print "+-------------------------------------------------+"
else:
print "No.%s %s %s %s %s" % (rownum,uc_name,str(execution),owner,manager)
print "+------------------------------------------------------------------+"
if 'answer' in locals() and answer=="":
answer= raw_input('\nAre you sure to send email to the owners?(yes or no)\n')
if 'answer' in locals():
if re.search(r'yes',answer,re.I):
bomb_push("yes");
print "Email has been sent, have a nice day!"
else :
print "Email won't be sent, see you!"
exit()
bomb_push("");
exit()
警告:此代码威力强大,请谨慎使用,后果自负。