虽然改用 java 做测试开发有段时间了,但之前在使用 python 过程中,还是写了不少小工具;
在此跟大家分享下 基于禅道统计 BUG 趋势图的 SQA 小工具 (python 版)

-----背景-----

当时技术总监要求我这边按周定期输出 每条产品的 BUG 情况,以此来反馈 开发解决问题、测试跟进问题的情况;当时公司内部有 5 大产品线,每条产品线至少 3 个以上产品在开发;如果按照手工方案一个一个来进行数据统计,那简直就是傻子行为了。以此我这边开始着手准备编写一个小工具,最终达到目的:自动定期发送统计报告,报告维度(数据 + 图表展示)

-----技术选型-----
python + excel + jenkins

-------达到目标--------------
1.导出当前一星期的 BUG 统计数据
2.导出指定时间 -- 当前时间的 BUG 统计数据
3.统计维度(新增、已解决、已关闭、未解决 (累计)、延期解决 (累计))
4.领导可通过报表直观的看到产品线情况

说明下各大概念指标,与常规的统计指标有一定区别:
按周统计:
1、新增,新增日期为本周内的(包括本周内被解决或关闭的 BUG)
2、已解决,解决日期为本周内的。被开发设定为已解决的。其中可能有部分是上周遗留下来的,体现了开发在本周的变化情况(包括设计如此、重复 BUG、外部原因、无法重现、不予解决、转为需求),不包含延期处理
3、已关闭,关闭日期为本周内的。是测试验证过,确实已经解决的,包括其中有的是上周遗留下来的
4、未解决,当前显示 BUG 状态为未解决的。包含当前还没被解决的、之前遗留的未解决、以及 reopen 的 BUG(累计数据)
5、延期解决,当前显示 BUG 状态为 延期处理的。BUG 状态中新增一个延期解决 (累计数据)
最终实现效果:



核心代码:

'''
    创建图表图形方法--按周
    '''
    def chart_series_week(self,sheet_name,type,row_len,col_len):
        chart = self.workbook.add_chart({'type': '%s'%(type)})
        if type == "pie":
            for j in range(2,col_len-2):
                chart.add_series({
                    'name':       ['%s'%(sheet_name), 0, j],
                    'categories': ['%s'%(sheet_name), 1, 0, row_len, 0],
                    'values':     ['%s'%(sheet_name), 1, j, row_len,j],
                    'data_labels': {'percentage': 1},   #百分比显示数值
                })
        else:
            for j in range(2,col_len-2):
                chart.add_series({
                    'name':       ['%s'%(sheet_name), 0, j],
                    'categories': ['%s'%(sheet_name), 1, 0, row_len, 0],
                    'values':     ['%s'%(sheet_name), 1, j, row_len,j],#显示数据
                    'data_labels': {'value': 1},#显示数据表
                })
        #添加数据表
        chart.set_table()
        # 设置图表风格.
        chart.set_style(18)
        #设置图表大小
        chart.set_size({'width': 650, 'height': 450})
        return chart

 '''
    创建图表图形方法-按产品或项目
    '''
    def chart_series_all(self,sheet_name,type,row_len,col_len):
        chart = self.workbook.add_chart({'type': '%s'%(type)})
        if type == "pie":
            for j in range(col_len-4,col_len):
                chart.add_series({
                    'name':       ['%s'%(sheet_name), 0, j],
                    'categories': ['%s'%(sheet_name), 1, 0, row_len, 0],
                    'values':     ['%s'%(sheet_name), 1, j, row_len,j],
                    'data_labels': {'percentage': 1},   #百分比显示数值
                })
        else:
            for j in range(col_len-4,col_len):
                chart.add_series({
                    'name':       ['%s'%(sheet_name), 0, j],
                    'categories': ['%s'%(sheet_name), 1, 0, row_len, 0],
                    'values':     ['%s'%(sheet_name), 1, j, row_len,j],#显示数据
                    'data_labels': {'value': 1},#显示数据表
                })
        #添加数据表
        chart.set_table()
        # 设置图表风格.
        chart.set_style(18)
        #设置图表大小
        chart.set_size({'width': 650, 'height': 450})
        return chart

'''
    柱形图
    哼哈BUG按周统计图
    @sheet_name: Sheet页名称
    @sql_date: 2016-01-04 00:00:00格式
    例:今天为2016-01-04 00:00:00,输入这个时间后,会自动查询2015-12-28 00:00:00---2016-01-03 23:59:59时间段内BUG
    '''
    def CountBUGAsWeeklyForHuaLa(self,sheet_name,sql_date):
        #计算开始时间和结束时间
        dateResult = op_date.week_get(sql_date)
        start_date = dateResult[0][0]
        end_date = dateResult[1][0]
        workbook = self.workbook
        worksheet = self.workbook.add_worksheet(name=sheet_name)
        bold = workbook.add_format({'bold': 1})
        # 定义数据表头列表

        # title = [u'按周统计图', u'统计日期',u'新增', u'已解决',u'已关闭',u'未解决(累计)',u'延期解决(累计)',u'已关闭(累计)',u'总BUG数']
        # buname = [u"哼哈微信端",u"哼哈商户端(android)",u"哼哈商户端(iOS)",u"哼哈后台",u"哼哈生活(产品)"]
        title = cn.bugStatusList
        buname = cn.huala_week
        #获取row长度
        row_len = len(buname)
        #获取col长度
        col_len = len(title)
        #定义数据列表
        #花啦微信端统计所有BUG

        data = []
        #添加哼哈微信用户端BUG数据
        result1 = cn.BugCountByProject(cn.hl_pjct[0],sql_date)
        data.append(result1)
        #添加哼哈商户端(android)BUG数据
        result2 = cn.BugCountByProject(cn.hl_pjct[1],sql_date)
        data.append(result2)
        #添加哼哈商户端(iOS)BUG数据
        result3 = cn.BugCountByProject(cn.hl_pjct[2],sql_date)
        data.append(result3)

        #添加哼哈运营后台BUG数据
        result4 = cn.BugCountByProject(cn.hl_pjct[3],sql_date)
        data.append(result4)

        #添加哼哈生活BUG数据
        result5 = cn.BugCountByProduct(cn.hl_pdct[0],sql_date)
        data.append(result5)

        format_title=workbook.add_format()    #定义format_title格式对象
        format_title.set_border(1)   #定义format_title对象单元格边框加粗(1像素)的格式
        format_title.set_bg_color('#cccccc')   #定义format_title对象单元格背景颜色为
                                               #'#cccccc'的格式
        format_title.set_align('center')    #定义format_title对象单元格居中对齐的格式
        format_title.set_bold()    #定义format_title对象单元格内容加粗的格式


        worksheet.write_row('A1', title, format_title)

        worksheet.write_column('A2', buname,bold)
        for i in range(2,row_len+2):
            worksheet.write_row('B%d'%(i),data[i-2])

        #创建一个图表,类型是column(柱形图)
        chart = self.chart_series_week(sheet_name,"column",row_len,col_len)

        # Add a chart title and some axis labels.
        chart.set_title ({'name': u'按周统计BUG %s--%s'%(start_date,end_date)})
        chart.set_x_axis({'name': u'BUG状态'})
        chart.set_y_axis({'name': u'BUG数'})

        # Insert the chart into the worksheet (with an offset).
        worksheet.insert_chart('A9', chart, {'x_offset': 25, 'y_offset': 10})


        #创建一个图表,类型是column(柱形图)
        chart1 = self.chart_series_all(sheet_name,"column",row_len,col_len)

        # Add a chart title and some axis labels.
        chart1.set_title ({'name': u'按产品或项目统计总BUG %s'%(end_date)})
        chart1.set_x_axis({'name': u'BUG状态'})
        chart1.set_y_axis({'name': u'BUG数'})

        # Insert the chart into the worksheet (with an offset).
        worksheet.insert_chart('L9', chart1, {'x_offset': 25, 'y_offset': 10})

时间处理

#coding = utf-8
import datetime
#a = datetime.datetime.now()
'''
Create by 古月随笔
'''
class op_date(object):
    def day_get(self,d):
        if type(d).__name__ == "str":
            d = datetime.datetime.strptime(d,'%Y-%m-%d %H:%M:%S')
        oneday = datetime.timedelta(days=1)
        day = d - oneday
        date_from = datetime.datetime(day.year, day.month, day.day, 0, 0, 0)
        date_to = datetime.datetime(day.year, day.month, day.day, 23, 59, 59)
        print '---'.join([str(date_from), str(date_to)])

    def week_get(self,d):
        if type(d).__name__ == "str":
            d = datetime.datetime.strptime(d,'%Y-%m-%d %H:%M:%S')
        dayscount = datetime.timedelta(days=d.isoweekday())
        dayto = d - dayscount
        sixdays = datetime.timedelta(days=6)
        dayfrom = dayto - sixdays
        date_from = datetime.datetime(dayfrom.year, dayfrom.month, dayfrom.day, 0, 0, 0)
        date_to = datetime.datetime(dayto.year, dayto.month, dayto.day, 23, 59, 59)
        datelist=[[str(date_from)],[str(date_to)]]
        # print '---'.join([str(date_from), str(date_to)])
        return datelist

    def multi_week_get(self,d,num):
        if type(d).__name__ == "str":
            d = datetime.datetime.strptime(d,'%Y-%m-%d %H:%M:%S')
        date_num = []
        # date_num = [date_to1]
        for i in range(num-1,0,-1):
            dayscount = datetime.timedelta(days=d.isoweekday())
            dayto = d - dayscount
            sixdays = datetime.timedelta(days=6*i)
            dayfrom = dayto - sixdays
            date_from = str(datetime.datetime(dayfrom.year, dayfrom.month, dayfrom.day, 10, 0, 0))
            date_num.append(date_from)

        dayscount1 = datetime.timedelta(days=d.isoweekday())
        onedays = datetime.timedelta(days=1)
        dayto1 = d - dayscount1 + onedays
        date_to1 = str(datetime.datetime(dayto1.year, dayto1.month, dayto1.day, 10, 0, 0))
        date_num.append(date_to1)
        return date_num

    def month_get(self,d):
        if type(d).__name__ == "str":
            d = datetime.datetime.strptime(d,'%Y-%m-%d %H:%M:%S')
        dayscount = datetime.timedelta(days=d.day)
        dayto = d - dayscount
        date_from = datetime.datetime(dayto.year, dayto.month, 1, 0, 0, 0)
        date_to = datetime.datetime(dayto.year, dayto.month, dayto.day, 23, 59, 59)
        # print '---'.join([str(date_from), str(date_to)])
        datelist=[[str(date_from)],[str(date_to)]]
        return datelist

if __name__ == "__main__":
    op_date = op_date()
    # print op_date.week_get("2016-01-09 23:00:00")[0]
    #print op_date.week_get("2016-01-3 23:00:00")[1]
    print op_date.multi_week_get("2015-12-23 23:00:00",4)

统计 BUG 的 SQL 语句

#coding=utf-8

from op_date import *
from op_mysql import *
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
'''
Create by 古月随笔
'''
class config(object):
    def __init__(self):
        self.op_date = op_date()
        # self.ms = op_mysql(host="127.0.0.1",user="root",pwd="",db="zentao")
        self.ms = op_mysql(host="192.168.200.60",user="root",pwd="123456",db="zentao")

    #BUG状态分类:
    bugStatusList = [u'按周统计图', u'统计日期',u'新增', u'已解决',u'已关闭',u'未解决(累计)',u'延期解决(累计)',u'已关闭(累计)',u'总BUG数']
    #xxxx产品&项目
    hengha = [[u"哼哈微信端"],[u"哼哈商户端(android)"],[u"哼哈商户端(iOS)"],[u"哼哈后台"],[u"哼哈生活(产品)"]]
    #花xxxxx产品&项目
    hengha_week = [u"哼哈微信端",u"哼哈商户端(android)",u"哼哈商户端(iOS)",u"哼哈后台",u"哼哈生活(产品)"]

    #ERP
    erpyuvmp = [[u"ERP2.0(产品)"],[u"CRM(产品)"],[u"VMP(产品)"]]
    #ERP
    erpyuvmp_week = [u"ERP2.0(产品)",u"CRM(产品)",u"VMP(产品)"]


    def BugCountByProject(self,projectNo,sql_date):
        ms = self.ms
        data = []
        projectNo = int(projectNo)
        date_result = self.op_date.week_get(sql_date)
        start_date = date_result[0][0]
        end_date = date_result[1][0]
        #查找一个星期内新增的BUG数openedDate 例如今天为2016-01-04 00:00:00,输入这个时间后,会自动查询2015-12-28 00:00:00---2016-01-03 23:59:59时间段内BUG
        AllNewBugCount_OneWeek = "select count(*) from zt_bug where project = '%d' and deleted = '0' and openedDate >= '%s' and openedDate <= '%s'"%(projectNo,start_date,end_date)
        #查找一个星期内已解决的BUG数(以最近的星期天为准,计算星期一到星期天,包含本周 解决到关闭的BUG) resolvedDate
        AllResolvedBugCount_OneWeek = "select count(*) from zt_bug where project = '%d' and deleted = '0' and `status` <> 'active' and resolution <> 'postponed' and resolvedDate >= '%s' and resolvedDate <= '%s'"%(projectNo,start_date,end_date)
        #查找所有未解决BUG数(以最近的星期天为准,计算星期一到星期天)(当前显示BUG状态为未解决的。包含当前还没被解决的、之前遗留的未解决、以及reopen的BUG(累计数据))
        AllNotResolvedBugCount = "select count(*) from zt_bug where project = '%d' and deleted = '0' and `status` =  'active' and openedDate <= '%s'"%(projectNo,end_date)
        #查找用户所有延期解决的问题
        AllPostponedBugCount = "select count(*) from zt_bug where project = '%d' and deleted = '0' and `status` <> 'closed' and resolution = 'postponed' and resolvedDate <= '%s'"%(projectNo,end_date)
        #查找 一个星期内已关闭的BUG数(以最近的星期天为准,计算星期一到星期天) closedDate
        AllClosedBugCount_OneWeek = "select count(*) from zt_bug where project  = '%d' and deleted = '0' and `status` = 'closed' and closedDate >= '%s' and closedDate <= '%s'"%(projectNo,start_date,end_date)

        #查找 已关闭BUG数(累计)
        AllClosedBugCount = "select count(*) from zt_bug where project  = '%d' and deleted = '0' and `status` = 'closed' and closedDate <= '%s'"%(projectNo,end_date)

        #查找 总BUG数
        AllBugCount = "select count(*) from zt_bug where project  = '%d' and deleted = '0' and openedDate <='%s'"%(projectNo,end_date)

        #新增
        dAllNewBugCount_OneWeek = ms.ExecQuery(AllNewBugCount_OneWeek)[0][0]
        #已解决
        dAllResolvedBugCount_OneWeek = ms.ExecQuery(AllResolvedBugCount_OneWeek)[0][0]
        #已关闭
        dAllClosedBugCount_OneWeek = ms.ExecQuery(AllClosedBugCount_OneWeek)[0][0]
        #未解决(累计数据)
        dAllNotResolvedBugCount = ms.ExecQuery(AllNotResolvedBugCount)[0][0]
        #延期解决(累计数据)
        dAllPostponedBugCount = ms.ExecQuery(AllPostponedBugCount)[0][0]
        #已关闭(累计)
        dAllClosedBugCount = ms.ExecQuery(AllClosedBugCount)[0][0]
        #总BUG数
        dAllBugCount = ms.ExecQuery(AllBugCount)[0][0]
        data = ["%s~%s"%(start_date[:-9],end_date[:-9]),dAllNewBugCount_OneWeek,dAllResolvedBugCount_OneWeek,dAllClosedBugCount_OneWeek,dAllNotResolvedBugCount,dAllPostponedBugCount,dAllClosedBugCount,dAllBugCount]
        return data


    def BugCountByProduct(self,productNo,sql_date):
        ms = self.ms
        data = []
        productNo = int(productNo)
        date_result = self.op_date.week_get(sql_date)
        start_date = date_result[0][0]
        end_date = date_result[1][0]
        #查找一个星期内新增的BUG数openedDate 例如今天为2016-01-04 00:00:00,输入这个时间后,会自动查询2015-12-28 00:00:00---2016-01-03 23:59:59时间段内BUG
        AllNewBugCount_OneWeek = "select count(*) from zt_bug where product = '%d' and deleted = '0' and openedDate >= '%s' and openedDate <= '%s'"%(productNo,start_date,end_date)
        #查找一个星期内已解决的BUG数(以最近的星期天为准,计算星期一到星期天) resolvedDate
        AllResolvedBugCount_OneWeek = "select count(*) from zt_bug where product = '%d' and deleted = '0' and `status` <> 'active' and resolution <> 'postponed' and resolvedDate >= '%s' and resolvedDate <= '%s'"%(productNo,start_date,end_date)
        #查找 一个星期内已关闭的BUG数(以最近的星期天为准,计算星期一到星期天) closedDate
        AllClosedBugCount_OneWeek = "select count(*) from zt_bug where product  = '%d' and deleted = '0' and `status` = 'closed' and closedDate >= '%s' and closedDate <= '%s'"%(productNo,start_date,end_date)

        #查找所有未解决BUG数(以最近的星期天为准,计算星期一到星期天)(当前显示BUG状态为未解决的。包含当前还没被解决的、之前遗留的未解决、以及reopen的BUG(累计数据))
        AllNotResolvedBugCount = "select count(*) from zt_bug where product = '%d' and deleted = '0' and `status` =  'active' and openedDate <= '%s'"%(productNo,end_date)
        #查找用户所有延期解决的问题
        AllPostponedBugCount = "select count(*) from zt_bug where product = '%d' and deleted = '0' and `status` <> 'closed' and resolution = 'postponed'and resolvedDate <= '%s'"%(productNo,end_date)
        #查找 已关闭BUG数(累计)
        AllClosedBugCount = "select count(*) from zt_bug where product  = '%d' and deleted = '0' and `status` = 'closed' and closedDate <= '%s'"%(productNo,end_date)

        #查找 总BUG数
        AllBugCount = "select count(*) from zt_bug where product  = '%d' and deleted = '0'and openedDate <='%s'"%(productNo,end_date)

        #新增
        dAllNewBugCount_OneWeek = ms.ExecQuery(AllNewBugCount_OneWeek)[0][0]
        #已解决
        dAllResolvedBugCount_OneWeek = ms.ExecQuery(AllResolvedBugCount_OneWeek)[0][0]
        #已关闭
        dAllClosedBugCount_OneWeek = ms.ExecQuery(AllClosedBugCount_OneWeek)[0][0]
        #未解决(累计数据)
        dAllNotResolvedBugCount = ms.ExecQuery(AllNotResolvedBugCount)[0][0]
        #延期解决(累计数据)
        dAllPostponedBugCount = ms.ExecQuery(AllPostponedBugCount)[0][0]
        #已关闭(累计)
        dAllClosedBugCount = ms.ExecQuery(AllClosedBugCount)[0][0]
        #总BUG数
        dAllBugCount = ms.ExecQuery(AllBugCount)[0][0]
        data = ["%s~%s"%(start_date[:-9],end_date[:-9]),dAllNewBugCount_OneWeek,dAllResolvedBugCount_OneWeek,dAllClosedBugCount_OneWeek,dAllNotResolvedBugCount,dAllPostponedBugCount,dAllClosedBugCount,dAllBugCount]

        return data
    """
    花啦生活:
        按project统计
        花啦生活-微信用户端:37
        花啦生活--商户端:39
        花啦生活--运营后台:38
    ERP&CRM(Product):
        按照产品编号来进行统计
        ERP2.0 : 3
        CRM:25
        VMP :7
    """
    '''
    花啦生活(project)
    '''
    #哼哈生活-微信用户端:37
    henghawx_pjct = 37
    #哼哈生活--商户端(android):39
    henghashandroid_pjct = 39
    #哼哈生活-商户端(IOS):64
    henghashios_pjct = 64
    #哼哈生活--运营后台:38
    henghayy_pjct = 38
    #哼哈生活 : 22(产品)
    hengha_pdct = 22

    hh_pjct = [henghawx_pjct,henghashandroid_pjct,henghashios_pjct,henghayy_pjct]
    hh_pdct = [hengha_pdct]

    '''
    ERP&CRM(Product):
    '''
    # ERP2.0 : 3
    erp_pdct = 3
    # CRM:25
    crm_pdct = 25
    # VMP :7
    vmp_pdct = 7
    erp_pdct_list = [erp_pdct,crm_pdct,vmp_pdct]

if __name__ == "__main__":
    cn = config()
    data = []
    result1 = cn.BugCountByProject(cn.henghawx_pjct,"2016-01-06 00:00:00")
    data.append(result1)
    result2 = cn.BugCountByProject(cn.henghash_pjct,"2016-01-06 00:00:00")
    data.append(result2)
    result3 = cn.BugCountByProject(cn.henghayy_pjct,"2016-01-06 00:00:00")
    data.append(result3)
    print data

GitHub 地址: git@github.com:tigerge000/SQA_BUG_Count.git


↙↙↙阅读原文可查看相关链接,并与作者交流