项目管理工具很多,我们公司一直用禅道做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定时跑任务执行

需要源码的可以私聊共享~~