JIRA自动化教程(Python爬虫)

这个脚本实现了:
  • 按指定的条件搜索出对应的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自动化

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,380评论 0 5
  • 肯定有方法。 而且这种方法一定会带着一点点个性化,而恰恰是这一点点就令到别人无从模仿。 橘过淮则为枳,越过某种界限...
    春风十里木有一路桃花阅读 188评论 0 3
  • 今天把老家搞了大扫除,全部杂物清理干净。 并且重新换了家具,床,衣柜,消毒柜全都换了,家里焕然一新,一些不做用的东...
    安福阅读 200评论 0 1
  • 一、印象最深刻是三件事 1、由于要听课,同学们今天坐的格外整齐。上课期间也没人玩手机,课堂秩序良好。 2、老师今天...
    小沐Julia阅读 218评论 1 0
  • 在这个社会上,我们常常感到世态炎凉,经常被人利用又被践踏,因此我们容易受伤。 正所谓:易涨易落山溪水,易反易覆小人...
    饥者求食阅读 666评论 2 18