我们每天的测试数据,需要及时反馈给领导,领导不喜欢看细节,就喜欢看结果,一目了然,快速又不费事。

领导最想看到就是  每个人的头上都有多少缺陷放着,大家上班的时候到底是不是在踏踏实实做事。报告真的很能说明事实。

测试组需要每天整理一份日报发送邮件给大家,从redmine(我司用的这款工具,相当好用啊,相比jira/禅道/bugzilla)上下载筛选过的缺陷列表,然后进行数据统计,重新处理模板报表···随着想要的信息越来越多,没半个小时这报告出不来,关键是有时候下班下得早,缺陷状态还不稳定(开发都还在处理),回家之后还要远程连接公司服务器。

反正就一个目的,为了这件事情能做得快点,大家早点下班,早点休息。

由于此版本是改版代码,存在一些多余的数据,后期修改

-----------

先来一些准备工作

其中有一些可能随时跟新的文本信息,单独放到txt,如下(注意文件名字要一致):

current.txt (每日更新的数据)

(此行代码不读)请在下面输入今日的git号:
9543a8c16e133f7a323d124e024622a59f938727
(此行代码不读)请按照格式[报表,谭秋,进度:30%;阻塞缺陷:3个;...]输入进展:
会员管理,李四,1.测试进度:48.6%;2.提交bug数:5个;3.提交的严重问题:无;4.阻塞进度的问题描述:无
信息管理,张三,1.测试进度:48.6%;2.提交bug数:5个;3.提交的严重问题:无;4.阻塞进度的问题描述:无
仓库管理,王五,1.测试进度:48.6%;2.提交bug数:5个;3.提交的严重问题:无;4.阻塞进度的问题描述:无

module.txt(项目所有的模块,会以此做统计,此处只举例三个)

会员管理
注册管理
系统管理

user.txt  (redmine 是姓与名 之间存在空格的存储,需要注意书写)

张 三
李 四
王 五
冯 宝宝

数据库结构:

你不想自己建的话,可以用这个sql:

/*
Navicat MySQL Data TransferSource Server         : 1
Source Server Version : 50728
Source Host           : 192.168.1.1:3306
Source Database       : reportTarget Server Type    : MYSQL
Target Server Version : 50728
File Encoding         : 65001Date: 2019-12-25 14:12:43
*/SET FOREIGN_KEY_CHECKS=0;-- ----------------------------
-- Table structure for all_data
-- ----------------------------
DROP TABLE IF EXISTS `all_data`;
CREATE TABLE `all_data` (`time_t` date NOT NULL,`new` int(255) unsigned zerofill DEFAULT NULL,`ongoing` int(255) unsigned zerofill DEFAULT NULL,`feedback` int(255) unsigned zerofill DEFAULT NULL,`delay` int(255) unsigned zerofill DEFAULT NULL,`reopen` int(255) unsigned zerofill DEFAULT NULL,`block` int(255) unsigned zerofill DEFAULT NULL,`refused` int(255) unsigned zerofill DEFAULT NULL,`untreated` int(255) unsigned zerofill DEFAULT NULL,`resolved` int(255) unsigned zerofill DEFAULT NULL,PRIMARY KEY (`time_t`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- ----------------------------
-- Table structure for daily_data
-- ----------------------------
DROP TABLE IF EXISTS `daily_data`;
CREATE TABLE `daily_data` (`time_t` date NOT NULL,`new` int(255) unsigned zerofill DEFAULT NULL,`ongoing` int(255) unsigned zerofill DEFAULT NULL,`feedback` int(255) unsigned zerofill DEFAULT NULL,`delay` int(255) unsigned zerofill DEFAULT NULL,`reopen` int(255) unsigned zerofill DEFAULT NULL,`block` int(255) unsigned zerofill DEFAULT NULL,`resolved` int(255) unsigned zerofill DEFAULT NULL,PRIMARY KEY (`time_t`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;-- ----------------------------
-- Table structure for git_msg
-- ----------------------------
DROP TABLE IF EXISTS `git_msg`;
CREATE TABLE `git_msg` (`time_t` date NOT NULL,`git_num` varchar(255) DEFAULT NULL,PRIMARY KEY (`time_t`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

准备好之后

附代码吧,你可以根据你的情况进行改动(由于此为改版,存在多余的操作,还望大家不要指责,后面会进行完善的)

# -*- coding:UTF-8 -*-
import xlwt
from datetime import datetime
from redminelib import Redmine
import pymysqlfile_user = 'user.txt'  # 参与人员名单
file_module = 'module.txt'  # 功能模块列表
file_curren = 'curren.txt'  # 每日进度和git数据
file_report = 'dailyreport.xls'   # 报表生成位置
time_list = ['2019-11-25', '2020-04-10']  # 设置数据获取时间段
#sql配置数据
conn_host='192.168.1.1'
conn_port=3306
conn_user='root'
conn_passwd='123456'
conn_db='report'# 缺陷对应人员的数量及缺陷模块数量
def current_day():resolve_user_list=[]module_list = []all_user_list =[]# userwith open(file_user, 'r',encoding='UTF-8') as file_user_read:user_lines = file_user_read.readlines()for user in user_lines:n = 0j = 0for issue in issues:if issue.assigned_to.name + '\n' == user:j +=1if issue.status.name == "已解决":n += 1resolve_user_list.append([user,n]) #已解决的人员分布all_user_list.append([user,j])# modulewith open(file_module, 'r', encoding='UTF-8') as file_module_read:module_lines = file_module_read.readlines()for module in module_lines:n = 0for issue in issues:if issue.custom_fields._resources[2]['value'] + '\n' == module:n += 1module_list.append([module,n])return resolve_user_list,module_list,all_user_list# current_day[0] 已解决人员对应# current_day[1] 模块对应# current_day[2] 人员对应def SandP_mysql():                    #2 3 6 7 点x = 0summer = [['时间','已解决','今日新建','待解决问题']]summer_daily = []summer_all = []march = [['模块名称', '测试人员', '测试内容及进度']]git_row = [['序号', '安装日期', 'branch: commit ID']]daily_row = [['时间','新建','进行中','已解决','反馈','阻塞','推迟','Reopen']]#daily_row.append([str(time_all), new_all, ongoing_all, resolved_all, feedback_all, block_all,delay_all, reopen_all])all_row = [['时间', '新建', '进行中', '反馈', '推迟', '重新打开', '阻塞', '已拒绝', '不处理', '已解决', '未修复']]i = 0with open(file_curren, 'r', encoding='UTF-8') as file_user_read:user_lines = file_user_read.readlines()for line in user_lines:i += 1if i == 2:daily_git_log = lineif int(i) > 3:line = line.split(',')march.append(line)# new 新增 ongoing 进行中 feedback 反馈 delay 推迟 reopen 重新打开 block 阻塞 closed 已关闭 refused 已拒绝 untreated 不处理 resolved 已解决 unrepaired 已验证new, ongoing, resolved, feedback, delay, reopen, block = 0, 0, 0, 0, 0, 0, 0new_all, ongoing_all, feedback_all, delay_all, reopen_all, block_all, refused_all, untreated_all, resolved_all = 0, 0, 0, 0, 0, 0, 0, 0, 0for issue in issues:if issue.status.name == "新建": new_all += 1if issue.status.name == "进行中": ongoing_all += 1if issue.status.name == "已解决": resolved_all += 1if issue.status.name == "反馈": feedback_all += 1if issue.status.name == "已拒绝": refused_all += 1if issue.status.name == "推迟": delay_all += 1if issue.status.name == "重新打开": reopen_all += 1if issue.status.name == "不处理": untreated_all += 1if issue.status.name == "阻塞": block_all += 1create_time = str(issue.created_on.year) + "-" + str(issue.created_on.month) + "-" + str(issue.created_on.day)solving = new_all+ongoing_all+ feedback_all+reopen_all+block_allif create_time == today:if issue.status.name == "新建": new += 1if issue.status.name == "进行中": ongoing += 1if issue.status.name == "已解决": resolved += 1if issue.status.name == "反馈": feedback += 1if issue.status.name == "推迟": delay += 1if issue.status.name == "重新打开": reopen += 1if issue.status.name == "阻塞": block += 1db = pymysql.connect(host=conn_host, port=conn_port, user=conn_user, passwd=conn_passwd, db=conn_db)cursor = db.cursor()sql_git_log = "replace into git_msg(time_t,git_num) VALUES ('%s','%s')" % (today,daily_git_log)sql_daily = "replace into daily_data(time_t,new,ongoing,resolved,feedback,delay,reopen,block) VALUES ('%s',%s,%s,%s,%s,%s,%s,%s)"%(today, new, ongoing, resolved, feedback, delay, reopen, block)sql_all = "replace into all_data(time_t,new,ongoing,feedback,delay,reopen,block,refused,untreated,resolved) VALUES ('%s',%s,%s,%s,%s,%s,%s,%s,%s,%s)"%(today, new_all, ongoing_all, feedback_all, delay_all, reopen_all, block_all, refused_all,untreated_all, resolved_all)try:cursor.execute(sql_git_log)db.commit()cursor.execute(sql_daily)db.commit()cursor.execute(sql_all)db.commit()print("ok")except:# 如果发生错误则回滚db.rollback()print("no")try:sql_daily = "SELECT * FROM daily_data WHERE time_t BETWEEN '%s' and '%s'" % (time_list[0], time_list[-1])sql_git = "SELECT * FROM git_msg WHERE time_t='%s'" % (today)sql_all = "SELECT * FROM all_data WHERE time_t BETWEEN '%s' and '%s'" % (time_list[0], time_list[-1])cursor.execute(sql_daily)  # 执行SQL语句results_daily = cursor.fetchall()  # 获取所有记录列表cursor.execute(sql_git)  # 执行SQL语句results_git = cursor.fetchall()  # 获取所有记录列表cursor.execute(sql_all)  # 执行SQL语句results_all = cursor.fetchall()  # 获取所有记录列表for row in results_daily:time_daily = row[0]new = row[1]summer_daily.append([str(time_daily), new])for row_all in results_all:time_all = row_all[0]new_all = row_all[1]ongoing_all = row_all[2]feedback_all = row_all[3]delay_all = row_all[4]reopen_all = row_all[5]block_all = row_all[6]refused_all = row_all[7]untreated_all = row_all[8]resolved_all = row_all[9]totle_unresolved = new_all+ongoing_all+feedback_all+delay_all+reopen_all+block_allall_row.append([str(time_all), new_all, ongoing_all, feedback_all, delay_all, reopen_all, block_all, refused_all,untreated_all,resolved_all,totle_unresolved])summer_all.append([resolved_all, totle_unresolved])daily_row.append([str(time_all), new_all, ongoing_all, resolved_all, feedback_all, block_all, delay_all, reopen_all])for i in range(0,len(summer_all)):summer_daily_vulue = list(zip(summer_daily[i],summer_all[i]))summer_li = []summer_li.append(summer_daily_vulue[0][0])summer_li.append(summer_daily_vulue[0][1])summer_li.append(summer_daily_vulue[1][0])summer_li.append(summer_daily_vulue[1][1])summer.append(summer_li)for git_line in results_git:x += 1time_daily = git_line[0]git_log = git_line[1]git_row.append([x,str(time_daily),git_log])except:print("Error: unable to fetch data")db.close()return git_row,march,summer,daily_row#  SandP_mysql [0]  git_num#  SandP_mysql [1]  测试进展#  SandP_mysql [2]  ticket汇总图#  SandP_mysql [3]  每日ticket状态分布def analysis():# 严重 grave 致命 deadly 紧急 urgent 已解决 resolved  超时 timeout 阻塞 stopanalysis_list = []become_list = [['序号', '关系人','ticket_ID', 'ticket详情']]  #退化缺陷new_num,become = 0,0grave_list  = [['序号', '状态','ticket_ID', 'ticket详情']]  # 重大缺陷列表stop_list = [['序号', '关系人','ticket_ID', 'ticket详情']]  #阻塞缺陷列表grave_num, deadly_num, timeout_num,stop_num,resolved_all = 0, 0, 0, 0, 0for issue in issues:update_time = str(issue.updated_on.year) + "-" + str(issue.updated_on.month) + "-" + str(issue.updated_on.day)updatetime_date = datetime.strptime(update_time, '%Y-%m-%d')today_date = datetime.strptime(today, '%Y-%m-%d')delta = today_date - updatetime_dateif delta.days >= 7: timeout_num += 1if issue.custom_fields._resources[0]['value'] == "重大" or issue.custom_fields._resources[0]['value'] == "致命": deadly_num += 1if issue.custom_fields._resources[3]['value'] == '1':become += 1become_list.append([become,issue.assigned_to.name, issue.id,issue.subject])if issue.custom_fields._resources[0]['value'] == "重大" and issue.priority['name'] == "紧急":stop_num += 1stop_list.append([stop_num,issue.assigned_to.name, issue.id,issue.subject])if issue.status.name == "已解决": resolved_all += 1create_time = str(issue.created_on.year) + "-" + str(issue.created_on.month) + "-" + str(issue.created_on.day)if create_time == today:new_num += 1if issue.custom_fields._resources[0]['value'] == "重大":grave_num += 1grave_list.append([grave_num,issue.status.name, issue.id, issue.subject])analysis_list.append(grave_list)  #重大缺陷列表 [0]analysis_list.append(stop_list)  #阻塞缺陷列表 [1]analysis_list.append(grave_num)  #重大缺陷个数 [2]analysis_list.append(deadly_num)  #重大/致命缺陷个数 [3]analysis_list.append(resolved_all) #已解决数 [4]analysis_list.append(timeout_num)  # 超过一周未更新数 [5]analysis_list.append(new_num)     #今日新增数 [6]analysis_list.append(become)     # 退化数 [7]analysis_list.append(become_list)  # 退化列表 [8]return analysis_listdef set_style(name, height,bold=False):style = xlwt.XFStyle()   # 初始化样式font = xlwt.Font()       # 为样式创建字体font.name = namefont.bold = bold  #粗体font.color_index = 4font.height = heightstyle.font = fontreturn styledef set_boder_style(name, height,bold=False):style = xlwt.XFStyle()   # 初始化样式font = xlwt.Font()       # 为样式创建字体font.name = namefont.bold = boldfont.color_index = 4font.height = heightstyle.font = fontborders = xlwt.Borders()borders.left = 1borders.right = 1borders.top = 1borders.bottom = 1style.borders = bordersreturn styledef sheet_write(row,sheet,n,style):for i in range(len(row)):for j in range(len(row[i])):sheet.write(n + i + 1, j, row[i][j], style)def write_csv():workbook = xlwt.Workbook(encoding='utf-8')data_sheet = workbook.add_sheet(today)boder_style = set_boder_style('Times New Roman', 220)unboder_style = set_style('Times New Roman', 220)data_analy=analysis()data_sql=SandP_mysql()data_BJS=current_day()row1 = [u'1.测试计划']row1_value = [['时间安排', '20190826-20191104'],['测试计划','http://xxx.xxx.87.87:19503/issues/62635'], ['人员安排', '张三、李四、王五']]row2 = [u'2.测试版本']row2_value = data_sql[0]  # gitrow3 = [u'3.测试进展']row3_value = data_sql[1]row4 = ['4.'+today + u'今日测试情况']row4_value1 = [['新增缺陷个数', data_analy[6]]] # , ['严重问题',data_analy[2]]]row5 = [u'ticket总量情况']row5_value1 = data_sql[2]    # 折线图 三个row5_value2 = data_sql[3]    #柱状图 竖着row5_value3 = [['超过一周未更新ticket数',data_analy[5],'退化的ticket数',data_analy[7],'致命/重大ticket数',data_analy[3],'已解决缺陷总数',data_analy[4],'今日新建ticket数',data_analy[6]]]row6 = [u'缺陷概况']row6_value1_title = [u'退化问题列表']row6_value1_value = data_analy[8]row6_value2_title = [u'严重问题列表']row6_value2_value = data_analy[0]row6_value3_title = [u'阻塞问题列表']row6_value3_value = data_analy[1]row7 = [u'所有缺陷人员对应']row7_value = data_BJS[2]row8 = [u'已解决缺陷分布']row8_value = data_BJS[0]row9 = [u'所有缺陷模块分布']row9_value = data_BJS[1]n = 0#---1----data_sheet.write(0, 0, row1, unboder_style)n += 1sheet_write(row1_value,data_sheet,n,boder_style)n += len(row1_value) + 1# ----2---data_sheet.write(n + 1, 0, row2, unboder_style)n += 1sheet_write(row2_value, data_sheet, n, boder_style)n += len(row2_value) + 1# ----3--data_sheet.write(n + 1, 0, row3, unboder_style)n += 1sheet_write(row3_value,data_sheet,n,boder_style)n += len(row3_value) + 1#---4-data_sheet.write(n + 1, 0, row4, unboder_style)n += 1sheet_write(row4_value1,data_sheet,n,boder_style)n += len(row4_value1) + 1# sheet_write(row4_value2, data_sheet, n, boder_style)# n += len(row4_value2) + 1# ----5---data_sheet.write(n + 1, 0, row5, unboder_style)n += 1sheet_write(row5_value1,data_sheet,n,boder_style)n += len(row5_value1) + 1sheet_write(row5_value2, data_sheet, n, boder_style)n += len(row5_value2) + 1sheet_write(row5_value3, data_sheet, n, boder_style)n += len(row5_value3) + 1# ----6--data_sheet.write(n + 1, 0, row6, unboder_style)n += 1data_sheet.write(n + 1, 0, row6_value1_title, unboder_style)n += 1sheet_write(row6_value1_value,data_sheet,n,boder_style)n += len(row6_value1_value) + 1data_sheet.write(n + 1, 0, row6_value2_title, unboder_style)n += 1sheet_write(row6_value2_value,data_sheet,n,boder_style)n += len(row6_value2_value) + 1data_sheet.write(n + 1, 0, row6_value3_title, unboder_style)n += 1sheet_write(row6_value3_value,data_sheet,n,boder_style)n += len(row6_value3_value) + 1# ---7----data_sheet.write(n + 1, 0, row7, unboder_style)n += 1sheet_write(row7_value,data_sheet,n,boder_style)n += len(row7_value) + 1#---8---data_sheet.write(n + 1, 0, row8, unboder_style)n += 1sheet_write(row8_value,data_sheet,n,boder_style)n += len(row8_value) + 1#------9----------data_sheet.write(n + 1, 0, row9, unboder_style)n += 1sheet_write(row9_value,data_sheet,n,boder_style)n += len(row9_value) + 1workbook.save(file_report)if __name__ == '__main__':redmine = Redmine('http://xxx.xxx.87.87:8080', username='username', password='password')issues = list(redmine.issue.filter(project_id=308, tracker_id=1, status_id='o', set_filter=1,fixed_version_id=986))time = datetime.now().timetuple()today = str(time.tm_year) + '-' + str(time.tm_mon) + '-' + str(time.tm_mday)today_date = datetime.strptime(today, '%Y-%m-%d')write_csv()

放在同一个目录下,运行这个python文件就可以出报告了

出来的结果是这样的,这些数据都是为了画趋势图

润色之后,就是这样的哟


从趋势图,可以结合自己项目的实际情况,看出缺陷的收敛情况,以及变化状态,阶段性的改变等等

以后 会考虑更多的功能进去,比如自动构图和发送报告等等,现在先发一个基础版。

对此有什么疑问或者意见,可留言~

python3实现自动生成redmine测试日报相关推荐

  1. [PHPUnit]自动生成PHPUnit测试骨架脚本-提供您的开发效率【2015升级版】

    2019独角兽企业重金招聘Python工程师标准>>> 场景 在编写PHPUnit单元测试代码时,其实很多都是对各个类的各个外部调用的函数进行测试验证,检测代码覆盖率,验证预期效果. ...

  2. java 日志 生成器_自动生成 java 测试 mock 对象框架 DataFactory-01-入门使用教程

    项目简介 Data-Factory 用于根据对象,随机自动生成初始化信息,避免了手动创建对象的繁琐,便于测试. 特性 8 大基本类型的支持 String.Date.金额,日期等常见类型的支持 java ...

  3. python3 实现自动生成入账记录表

    一.目的 动手写这个小功能主要出于简化工作量,提高工作效率.由于支付行业,每一笔资金的入款和出款都非常重要,所以测试过程中涉及到真实交易都需要记录,并发送给清算部门,方便资金的核对.如果某天测试数据量 ...

  4. php自动生成phpunit,[PHPUnit]自动生成PHPUnit测试骨架脚本

    场景 在编写PHPUnit单元测试代码时,其实很多都是对各个类的各个外部调用的函数进行测试验证,检测代码覆盖率,验证预期效果.为避免增加开发量,可以使用PHPUnit提供的phpunit-skelge ...

  5. [快速入门]Spring Boot+springfox-swagger2 之RESTful API自动生成和测试

    Swagger是自动生成 REST APIs文档的工具之一.Swagger支持jax-rs, restlet, jersey.springfox-swagger是Spring生态的Swagger解决方 ...

  6. IntelliJ IDEA快速自动生成Junit测试类

    1.背景 测试是保证代码必不可少的环节,自己构建测试方法太慢,并且命名也不规范,idea中提供了,一键构建测试结构的功能...废话不多说,直接写步骤 2.步骤 1.在需要做测试的类的当前窗口,直接按快 ...

  7. 自动生成用于测试和评估自动驾驶汽车的各种挑战性场景

    元模型 K近邻检测(KNN) 拉丁超立方体抽样 模型度量 高斯过程回归模型 自适应采样 信息熵(信息的不确定性越大 熵也越大) 摘要 我们提出了一种用于为黑匣子自治系统生成测试场景的新颖方法,该方法演 ...

  8. Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据

    一.简介 在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题: 问题一.后端接口查看难:要怎么调用?参数怎么传递?有几个参数?参数都代表什么含义? 问题二 ...

  9. idea swagger生成接口文档_Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据...

    一.简介 在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题: 问题一.后端接口查看难:要怎么调用?参数怎么传递?有几个参数?参数都代表什么含义? 问题二 ...

最新文章

  1. 压力测试的几种常见性解决方案
  2. selenium定位方法
  3. java web基本流程
  4. [转]浅析DDD(领域驱动设计)
  5. linux 制作box文件夹,用busybox制作自己简易的根文件系统
  6. 笔记 | 《机器学习》中特征选择与稀疏学习
  7. jquery blockui 遮罩【转】
  8. 第二季-专题3-汇编语言得玩转
  9. Ucinet软件使用
  10. 《MLB棒球创造营》:走近棒球运动·洛杉矶天使队
  11. 公网ip+内网穿透+c_str()+linux c++读文件+路径
  12. JAVA 图片格式转换 jpg、jpeg、png格式互转
  13. Mediawiki安装经验分享
  14. 小虫子的冒险_Python迷宫类型游戏
  15. VPN --- (虚拟专用网) 详解
  16. python sample函数取样_Pytorch各种取样器sample
  17. GSI计算机,DAT或者GSI水准数据用科傻(COSA)软件平差
  18. OpenSSL SSL_read: Connection was aborted, , errno 10053
  19. 【C#】Label不显示下划线
  20. Android 8.0 电池显示,电池定制

热门文章

  1. 牛B的Mathematica:寻找开头和结尾都是字母y的形容词
  2. Kepware OPC与欧姆龙PLC通讯
  3. 小强不一样的2015总结,不煽情不装逼,只是很直白而已
  4. c语言中w 是什么意思6,c/w certificate是什么意思
  5. 带你打造一套 APM 监控系统(四)
  6. GEE地物类型分类,提取某一地物
  7. 彩色宝石项链(环状链表处理)
  8. 关于Firefox无法代理本地localhost数据包,BP拦截不到本地IP的问题与解决
  9. 设计UI - Adobe xd文本和钢笔
  10. 打印机安装后显示服务器图标,WIN10系统已安装打印机却看不到图标的处理教程...