这个脚本实现了:
- 按指定的条件搜索出对应的Jira列表,并将这些Jira的所有信息读取出来;
- 提取出有效信息,并按照写好的html模板将信息填入其中;
- 将该邮件发给Jira列表里包括的assignee人员和配置的默认收件人;
Python-jira自动化实现代码
-
获取Jira内容
# encoding=utf-8
# ----------------------------------------
# 语言:Python2.7
# 功能:jira模板,含登录和获取列表
# ----------------------------------------
import base64
import json
from jira import JIRA
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
JIRA_SERVER = 'http://jira.n.company.com' # jira服务器
JIRA_FIELDS = 'issuetype,issuekey,summary,priority,status,resolution,assignee,reporter,created,duedate,updated'
MAX_RESULTS = 100 # 最多读取100条记录
# jira登录
def _login():
login_info = json.load(open('../configurations/login.conf')) # 加载登录信息
# 需要先解码再使用
login_info['user'] = base64.decodestring(login_info['user'])
login_info['pwd'] = base64.decodestring(login_info['pwd'])
basic_auth = (login_info['user'], login_info['pwd'])
try:
jira = JIRA(server=JIRA_SERVER, basic_auth=basic_auth)
except Exception, e:
print "Jira登录异常:" + e.message
else:
return jira
# 根据条件查找并返回issues
def get_issues(conditions):
jira = _login()
dict_list = []
conditions = conditions.split(';') # 支持多个条件组合查询,以;分割
for condition in conditions:
issues = jira.search_issues(condition, maxResults=MAX_RESULTS, fields=JIRA_FIELDS)
for issue in issues:
issue_type = str(issue.fields.issuetype)
issue_key = str(issue)
summary = str(issue.fields.summary.encode('utf-8'))
priority = str(issue.fields.priority)
status = str(issue.fields.status)
resolution = str(issue.fields.resolution)
assignee = str(issue.fields.assignee)
reporter = str(issue.fields.reporter)
created = str(issue.fields.created)[0:19] # 截取时间
due_date = str(issue.fields.duedate)[0:19]
updated = str(issue.fields.updated)[0:19]
issue_link = 'http://jira.n.company.com/browse/' + issue_key
address = str(issue.fields.assignee.emailAddress)
display_name = str(issue.fields.assignee.displayName)
# 创建jira字典并返回
issue_dict = {
'issuetype': issue_type,
'issuekey': issue_key,
'summary': summary,
'priority': priority,
'status': status,
'resolution': resolution,
'assignee': assignee,
'reporter': reporter,
'created': created,
'duedate': due_date,
'updated': updated,
'issuelink': issue_link,
'address': address,
'displayName': display_name
}
dict_list.append(issue_dict)
return dict_list
# 按照assignee排列搜索出issues,返回字典格式
def get_issues_by_assignee(conditions):
sorted_issues = get_issues(conditions)
print sorted_issues
print "get issues by assignee"
issues_by_assignee = {} # key:assignee,value:jiras of assignee
for issue in sorted_issues:
assignee = issue['assignee']
if assignee in issues_by_assignee.keys():
issues_by_assignee[assignee].append(issue)
else:
temp_dict = []
temp_dict.append(issue)
issues_by_assignee[assignee] = temp_dict
print str(issues_by_assignee)
if issues_by_assignee is None:
print "issues_by_assignee is null"
return issues_by_assignee
# 获取去重后的收信人列表
def get_cc_address(jira_dict):
cc_address = ''
for jira in jira_dict:
address = jira['address']
if address not in cc_address:
cc_address += address + ";"
return cc_address
# 获取收信人姓名
def get_cc_assignees(jira_dict):
cc_assignees = ''
for jira in jira_dict:
assignee = jira['assignee']
if assignee not in cc_assignees:
cc_assignees += "@" + jira['displayName'] + " " # jira里的姓名以@分隔
return cc_assignees
-
发邮件
# encoding=utf-8
# ----------------------------------------
# 语言:Python2.7
# 功能:邮件模板
# ----------------------------------------
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from jira_module import get_cc_address, get_cc_assignees
import json
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
DEFAULT_SENDER = 'suyongsheng@163.com' # 默认发送者
# 读取邮件内容配置
def get_email_conf(conf_name):
try:
email_conf = json.load(open(conf_name))
except Exception, e:
print "读取Json文件异常:" + e.message
else:
return email_conf
def get_t_table(jira_dict):
t_table = ''
for jira in jira_dict:
email_temp = '''
<tr>
<td><span class="check">%s</span></td>
<td><a href=%s class="check">%s</a></td>
<td><span class="check">%s</span></td>
<td><span class="check">%s</span></td>
<td><span class="check">%s</span></td>
<td><span class="check">%s</span></td>
<td><span class="check">%s</span></td>
<td><span class="check">%s</span></td>
<td><span class="check">%s</span></td>
</tr>
''' % (jira['issuetype'],jira['issuelink'],jira['issuekey'],jira['summary'],jira['priority'],
jira['assignee'],jira['reporter'],jira['created'],jira['updated'],jira['duedate'])
t_table += email_temp
return t_table
# email 模块
def send_email(email_dict, jira_dict):
if email_dict is None:
print "读取到的email配置为空"
return
print "[" + email_dict['subject'] + "] 正在构造邮件,请稍等..."
if email_dict['content']:
content = email_dict['content']
else:
content = '[默认提示]以下Jira需要被关注'
t_table = get_t_table(jira_dict)
cc_address = get_cc_address(jira_dict)
cc_assignees = get_cc_assignees(jira_dict)
subject = email_dict['subject']
receiver = cc_address + email_dict['defaultReceiver'] # 将爬取到的assignee添加到收件人列表
to_addrs = receiver.split(';')
# 构造邮件对象MIMEMultipart对象
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
# 收件人为多个收件人,通过join将列表转换为以,为间隔的字符串
msg['To'] = ",".join(to_addrs)
# 构造html
html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jira自动汇总</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
body{
font: italic 20px Georgia, serif;
letter-spacing: normal;
background-color: #f0f0f0;
}
#content{
width: 800px;
padding: 40px;
}
#table1{
font: bold 16px/1.4em "Trebuchet MS", sans-serif;
}
#table1 thead th{
padding: 15px;
border: 1px solid #93CE37;
border-bottom: 3px solid #9ED929;
text-shadow: 1px 1px 1px #568F23;
color: #fff;
background-color: #9DD929;
border-radius: 5px 5px 0px 0px;
}
#table1 thead th:empty{
background-color: transparent;
border: none;
}
#table1 tbody th{
padding: 0px 10px;
border: 1px solid #93CE37;
border-right: 3px solid #9ED929;
text-shadow: 1px 1px 1px #568F23;
color: #666;
background-color: #9DD929;
border-radius: 5px 0px 0px 5px;
}
#table1 tbody td{
padding: 10px;
border: 2px solid #E7EFE0;
text-align: center;
text-shadow: 1px 1px 1px #fff;
color: #666;
background-color: #DEF3CA;
border-radius: 2px;
}
</style>
</head>
<body>
<b>原因 : </b>%s<br>
<b>名单 : </b><span style="color:blue;font-weight:bold"><b>%s</b></span><br/><br/>
请在列同学及时关注并更新 jira状态.( 本邮件为自动发送,请勿回复 )
<div id="content">
<table id="table1">
<thead>
<tr>
<th scope="col" abbr="Starter">Issue Type</th>
<th scope="col" abbr="Starter">Key</th>
<th scope="col" abbr="Medium">Summary</th>
<th scope="col" abbr="Business">Priority</th>
<th scope="col" abbr="Deluxe">Assignee</th>
<th scope="col" abbr="Deluxe">Reporter</th>
<th scope="col" abbr="Deluxe">Created</th>
<th scope="col" abbr="Deluxe">Updated</th>
<th scope="col" abbr="Deluxe">Due Date</th>
</tr>
</thead>
<tbody>
%s
</tbody>
</table>
</div>
<address>
Written by <a href="mailto:suyongsheng@163.com">Su Yongsheng</a>.<br>
Visit me at : Xiaobailou 203<br>
</address>
</body>
</html>
""" % (content, cc_assignees, t_table)
text_html = MIMEText(html, 'html', 'utf-8')
msg.attach(text_html)
try:
smtp = smtplib.SMTP()
smtp.connect("mail.srv")
smtp.sendmail(DEFAULT_SENDER, to_addrs, msg.as_string())
smtp.close()
print "[%s] send email success" % subject
except Exception, e:
print "Error:[%s] unable to send email becauseof " % subject + e.message
# 报警邮件
def send_alarm_email(errMsg):
subject = 'Jira自动化报警邮件'
receiver = 'suyongsheng@163.com'
to_addrs = receiver.split(';')
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['To'] = ",".join(to_addrs)
# 构造html
html = """
<!DOCTYPE html>
<html>
<body>
<h1 id="blink"><b>Jira邮件发送异常</b></h1>
<p><b><font size="5">报警原因:%s</font></b></p>
<script language="javascript">
function changeColor(){
var color="#f00|#000";
color=color.split("|");
document.getElementById("blink").style.color=color[parseInt(Math.random() * color.length)];
}
setInterval("changeColor()",200);
</script>
</body>
</html>
""" % (errMsg)
text_html = MIMEText(html, 'html', 'utf-8')
msg.attach(text_html)
try:
smtp = smtplib.SMTP()
smtp.connect("mail.srv")
smtp.sendmail(DEFAULT_SENDER, to_addrs, msg.as_string())
smtp.close()
print "[%s] send alarm email success" % subject
except Exception, e:
print "Error:[%s] unable to send alarm email becauseof " % subject + e.message
-
获取日期
# encoding=utf-8
# ----------------------------------------
# 语言:Python2.7
# 功能:获取日期&时间
# ----------------------------------------
import datetime
NOW = datetime.datetime.now()
# 获取当前月份
def get_month():
month = NOW.month
return month
# 获取当前日期
def get_day():
day = NOW.day
return day
# 获取当前小时
def get_hour():
hour = NOW.hour
return hour
# 获取当前分钟
def get_minute():
minute = NOW.minute
return minute
# 获取当前星期
def get_weekday():
weekday = NOW.weekday() + 1 # 1~7:分别代表周一~周日
return int(weekday)
-
从zookeeper读取配置
# encoding=utf-8
# ----------------------------------------
# 语言:Python2.7
# 功能:从zookeeper读取配置
# 位置:/company/cash/jira/config
# ----------------------------------------
import zookeeper
from data_module import get_hour
# 获取zookeeper配置
def get_zk_data():
zk = zookeeper.init("zk.staging.srv:8080") # 初始化zookeeper
try:
zk_children = zookeeper.get_children(zk, "/company/cash/jira")
# 如果test节点存在,则不会运行config节点,方便对新配置进行测试
if 'test' in zk_children:
zk_test = zookeeper.get(zk, "/company/cash/jira/test")
if zk_test[0] is not '':
print '测试节点不为空,将使用测试节点的配置...'
return zk_test[0]
except Exception, e:
print e.message
# 目前只判断了test和config两个节点,后期如果有较多人使用,则添加扫描其他节点
zk_config = zookeeper.get(zk, "/company/cash/jira/config")
zookeeper.close(zk)
return zk_config[0]
# 清空test配置,保证每天定时能够顺利执行
def del_test_data():
hour = int(get_hour())
zk = zookeeper.init("zk.staging.srv:8080")
try:
if 0 <= hour <= 7: # 00:00~08:00之间的test配置会被清空
zk_children = zookeeper.get_children(zk, "/company/cash/jira")
if 'test' in zk_children:
zk_test = zookeeper.get(zk, "/company/cash/jira/test")
# 如果test节点存在且不为空,则清空配置
if zk_test[0] is not '':
zookeeper.set(zk, "/company/cash/jira/test", "")
except Exception, ex:
print ex.message
zookeeper.close
-
配置格式
{
"reward_system": {
"key": "reward_system",
"subject": "奖惩制度-Jira汇总",
"defaultReceiver": "cashtest@163.com",
"conditions": "type in (Bug) AND project in (SONG, FSK, HQ, HLTEL) AND status not in (Closed,Resolved) AND component in (现金) AND (priority = Blocker AND created <= -4d OR priority = Critical AND created <= -12d OR priority = Major AND created <= -115d OR priority = Minor AND created <= -175d)",
"content": "涉及到奖惩制度, 出现在下面 Jira列表的各位请及时更新.",
"singleOrBatch": "batch",
"triggerDay": "0",
"status": "VALID",
"memo": "奖惩制度-Jira汇总,2018-07-04开始此任务"
}
}
流程:开始任务 - 从zookeeper读取配置 - 自动登录Jira并根据搜索条件获取Jira列表 - 爬取Jira内容并组装邮件 - 发邮件 - 结束
完整代码可参考Github :Jira自动化