禅道数据的统计【源代码在文末】
项目管理工具很多,我们公司一直用禅道做bug管理,随便把项目管理也搞到禅道上,从需求建立--立项--分任务--执行任务--完成,用的是开源版本,所以少了很多报表,用过一段时间专业版,统计报表数据也不怎么好用,报表都不是自己想要的。
所以自己用python搞了一套统计:
简单模块:
1.各种报表分个类,比如bug的,周报,日报,项目统计等
2.common,处理table,发邮件,链接数据库等
3.sql_script,存放数据库脚本的
贴几个项目代码【需要的拿去用】:
## sql内部用的,数据量不大,没怎么优化,怎么方便怎么写
#BUG每天分布情况 SELECT z.`部门组`, z.`被指派`, CONCAT( z.提交人, '(', t.realname, ')' ) 提交人, z.`项目名称`, z.`Bug标题`, z.Bug状态, z.提交时间 FROM ( SELECT d.`name` 部门组, CONCAT( a.assignedTo, '(', u.realname, ')' ) 被指派, a.openedBy 提交人, a.`name` 项目名称, CONCAT(a.id, '--', a.title) Bug标题, CASE WHEN a.`status` = 'resolved' THEN '已解决' WHEN a.`status` = 'active' THEN '激活' WHEN a.`status` = 'closed' THEN '已关闭' END Bug状态, a.openedDate 提交时间 FROM ( SELECT b.id, p.`name`, b.title, b.`status`, b.openedBy, b.openedDate, CASE WHEN LENGTH(b.resolvedBy) > 0 THEN b.resolvedBy ELSE b.assignedTo END assignedTo, b.resolvedBy FROM zt_bug AS b, zt_project AS p WHERE b.openedDate >= CURDATE() # DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND b.project NOT IN ('107') AND b.project = p.id AND b.deleted = '0' ) AS a, zt_user AS u, zt_dept AS d WHERE a.assignedTo = u.account AND u.dept = d.id ) AS z, zt_user AS t WHERE z.`提交人` = t.account ORDER BY 部门组 ASC, 被指派 ASC, Bug状态 ASC
# -*- coding: UTF-8 -*- # 表列合并算法 import numpy as np # 统计相同的列的值及个数 def find_same_column(l, end=0, exc=''): """ :param l: 需要计算的列表 :param end: 需要合并的列数(前end列合并),默认为0列计算 :param exc: 列需要排除合并的值,默认为所有值都不合并 :return al: 返回计算好的列表 """ al = [] for i in range(len(l)): # if end == 0: # end = 100000 stop = end if exc == '': exc = '!@#$%^&*' logo = str(exc) last = '' num = 1 part = [] for x in range(len(l[i])): # 只合并列数范围 if i < stop: # 列值和标识值相同则不合并 if str(l[i][x]) != str(logo): # 找相同的列值,相等则计数+1 if l[i][x] == last: num += 1 # 如果列表循环完则append计数 if x + 1 == len(l[i]): part.append(str(num)) # 如果列值不同则更新last值并重置计数位 else: # 上一个列表的num没有append,这里append # 如果是列表第一个则不更新,如果列表上一个值与标识相同也不更新 if x != 0 and str(l[i][x-1]) != str(logo): part.append(str(num)) last = l[i][x] part.append(last) num = 1 # 如果列表循环完append计数 if x+1 == len(l[i]): part.append(str(num)) # 列表值与标识为相同不合并,直接append else: # 还是要判断是否append num if x != 0 and str(l[i][x-1]) != str(logo): part.append(str(num)) part.append(l[i][x]) part.append(str(1)) else: part.append(l[i][x]) part.append(str(1)) al.append(part) return al # 处理td标签 def deal_td(l): """ :param l: 需要处理td标签的列表 :return al: 返回处理好的列表 """ al = [] for i in range(len(l)): part = [] for x in range(0, len(l[i]), 2): if x == len(l[i]): break t_value = str(l[i][x]) t_sum = int(l[i][x+1]) if t_sum >= 2: for num in range(t_sum): if num == 0: part.append('<td rowspan="%s">' % str(t_sum) + t_value + '</td>') else: part.append('') else: part.append('<td>' + t_value + '</td>') al.append(part) return al # 处理tr标签 def deal_tr(l): """ :param l: 需要处理tr的列表 :return: 返回处理好的tr字符串 """ # 倒置列表 td = np.tile(l, 1).T.tolist() tr = '' for i in td: l_tr = '' for x in range(len(i)): l_tr += i[x] s_tr = '<tr>' + l_tr + '</tr>' + '\n' tr += s_tr return tr
# coding = utf-8 # 禅道项目度量--每日bug激活统计 import sys import datetime from common import common_mail_test as mail from common import common_read_sql as read_sql from common import common_mysql_config as mysql # 数据获取 def deal_mysql(f): """ 数据获取与处理 :param f: :return: """ cursor = mysql.db.cursor() sql = read_sql.read_sql(f) cursor.execute(sql) results = cursor.fetchall() cursor.close() mysql.db.close() # tuple类型转换为list类型 li = list(results) """ 把id相同的数据的放入pl列表,再把列表放入al列表 """ al = [] pl = [] for i in range(len(li)): if i == 0: pl.append(li[i]) # 如果和列表里的名字相同,继续append if li[i][0] == li[i - 1][0]: pl.append(li[i]) # 如果不相同,没有到末尾,把当前列表加入al列表,并清空pl列表/i-1 >0 排除i=0情况 if li[i][0] != li[i - 1][0] and i - 1 > 0 and len(pl) != 0: al.append(pl) pl = [] if i != len(li) - 1: # 如果和之前的不同,和之后的也不同,且不是末尾: if li[i][0] != li[i - 1][0] and i - 1 > 0 and li[i][0] != li[i + 1][0]: pl.append(li[i]) al.append(pl) pl = [] # 如果和之前的不同,和之后的相同,且不是末尾: if li[i][0] != li[i - 1][0] and i - 1 > 0 and li[i][0] == li[i + 1][0]: pl.append(li[i]) # 如果是末尾 if i == len(li) - 1: # pl.append(li[i]) # 后来发现重复插入最后一条数据 先注释 al.append(pl) # 只取满足日期的数据 el = [] for i in al: for x in i: if str(x[3]).split(' ')[0] == str(datetime.datetime.now()).split(' ')[0]: el.append(i) break return el # 处理html def deal_html(tl): """ 生成html :param tl: :return: """ subject = 'BUG激活统计' head = """ <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>BUG被激活</title> <body> <div id="container"> <center> <strong>汇总时间: """ + str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + """</strong> <p><strong>备注:BUG被激活次数大于等于3次</strong></p> <div id="content"> <table border="1" cellpadding="2" style="border-collapse:collapse;margin-top:10px"> <tr> <td colspan="5" align="center" height="35px"><font size="5"><strong>BUG激活情况</strong></font></td> </tr> """ ht = '' for i in tl: zj = '' beg = '' a = '' b = '' c = '' title = """ <tr> <td colspan="5" width="120" align="left"><font size="3" height="30px" ><strong>BUG标题:</strong> <a href="http://chandao.thecover.cn/zentao/bug-view-%s.html">%s</a></font></td> </tr> """ % (i[0][0], i[0][4]) for x in range(len(i)): ti = """ <tr> <td rowspan="%s" width="80" align="center"><font size="3"><strong>ID</strong></font></td> <td width="100" align="center"><font size="3"><strong>操作人</strong></font></td> <td width="100" align="center"><font size="3"><strong>解决类型</strong></font></td> <td width="180" align="center"><font size="3"><strong>操作时间</strong></font></td> <td width="700" align="left"><font size="3"><strong>备注</strong></font></td> </tr> """ if x == 0: beg = """ <tr> <td rowspan="%s" width="80" align="center"><font size="3">%s</font></td> <td width="100" align="center"><font size="3">%s</font></td> <td width="100" align="center"><font size="3">%s</font></td> <td width="180" align="center"><font size="3">%s</font></td> <td width="700" align="left"><font size="3">%s</font></td> </tr> """ % (len(i), i[x][0], i[x][1], i[x][2], i[x][3], i[x][5]) else: a = """<tr><td width="100" align="center"><font size="3">%s</font></td>""" % i[x][1] if i[x][2] == 'activated': b = """<td width="100" align="center"><font size="3" color="red">%s</font></td>""" % i[x][2] else: b = """<td width="100" align="center"><font size="3">%s</font></td>""" % i[x][2] c = """ <td width="180" align="center"><font size="3">%s</font></td> <td width="700" align="left"><font size="3">%s</font></td> </tr> """ % (i[x][3], i[x][5]) zj += a + b + c ho = """ <tr> <td colspan="5" width="120" align="right" ><font size="3" color="white">.</font></td> </tr> """ ht += title + ti + beg + zj + ho end = """ </table> <p><font size="3" ><center>--------------------<strong><a href="http://chandao.thecover.cn/">汇总数据源于禅道系统</a> </strong>--------------------</center></font> </center> </div> </div> </div> </body> </html> """ html = head + ht + end return subject, html mysql_l = deal_mysql(sys._getframe()) if len(mysql_l) > 0: s, h = deal_html(mysql_l) try: if mail.cs_mail_send(s, h): print('Send success') else: print('Send failure') except Exception as e: raise e else: print("NO Data !")
统计展示:
配置jenkins定时跑任务执行
源代码下载地址:
https://download.csdn.net/download/tengdakuaijie1/12272565
本文来自博客园,作者:drewgg,转载请注明原文链接:https://www.cnblogs.com/drewgg/p/11792150.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?