自制算法报表
好几年前写的代码, 其中javascript代码找专业人员写的。
#######页面
_HTML = '''
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>效果分析%(bizdate)s</title>
<link href='https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext' rel='stylesheet' type='text/css' />
<script type="text/javascript" src="https://gw.alipayobjects.com/os/lib/jquery/3.5.0/dist/jquery.min.js"></script>
</head>
<style>
html {overflow-x: initial !important;}:root { --bg-color: #ffffff; --text-color: #333333; --select-text-bg-color: #B5D6FC; --select-text-font-color: auto; --monospace: "Lucida Console",Consolas,"Courier",monospace; }
html { font-size: 12px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
.one{width:1400px; margin:0 auto;}
.mytable{margin:0 auto;}
table {border-collapse: collapse;}
table tr:nth-child(2n),thead { background-color: #f8f8f8;}
table tr th {
font-weight: bold;
border: 1px solid #dfe2e5;
padding: 3px 6px;font-size: small;
text-align: right;
}
table tr td {
border: 1px solid #dfe2e5;
padding: 3px 6px; font-size: x-small;
text-align: right;
white-space: nowrap; max-width: 100px; overflow:hidden; /* 文本太长处理 */
}
table tr td:hover {white-space: pre-wrap;} /* 鼠标移上,显示全文 */
.caption {width:850px; margin:0 auto; font-size: 16px;text-align: left;}
.caption a {font-size: 12px;}
.dataframe-div {max-height: 900px;
overflow: auto; position: relative;}
.dataframe thead th {
position: -webkit-sticky; /* for Safari */
position: sticky; top: 0;
background: lightblue; color: black;}
.dataframe thead th:first-child {left: 0; z-index: 1; }
.dataframe tbody tr th:only-of-type {vertical-align: middle;}
.dataframe tbody tr th {position: -webkit-sticky; /* for Safari */
position: sticky;left: 0;vertical-align: top;}
</style>
<body><div class="one">
<h2 style="text-align: center;"> 算法效果报告 bizdate=%(bizdate)s </h2>
<h4 style="width:1200px; margin:0 auto;">
%(tabs)s
</h4>
<div class="dataframe-div">%(contents)s</div>
<h2 style="text-align: center; color:#F00">注意数据安全, 请勿转发!!</h2>
<h4 style="text-align: center; ">有问题联系@xxx</h4>
</div>
<script type="text/javascript">
function search(table_id) {
return function(){
var value = $(this).val();
var patt = new RegExp(value, "i");
$(table_id).find('tbody').find('tr').each(function() {
if (!($(this).find('td').text().search(patt) >= 0)) {
$(this).hide();
}else{
$(this).show();
}
});
}
}
// 调用搜索script函数
// $('#s0').on('keyup', search('#t_id_0'));
%(calls)s
</script>
<script type="text/javascript">
const $body = $('body');
const tableData = [];
const tableSortInfo = []; // 记录每个表格排序参数
const numberReg = /^(-?\d+)(\.\d+)?$/; // 判断是否是数值类型的正则表达式
function convertToTbody(data) {
return data.map((row) => {
const tdStrs = row.map(entry => `<td>${entry}</td>`);
return `<tr>${tdStrs.join('')}</tr>`;
}).join('');
}
function getSorter(index, type) {
if (type === 'asc') {
// 升序排序
return function(rowA, rowB) {
if (rowA[index] === rowB[index]) {
return 0;
}
if (typeof rowA[index] === 'number') {
return rowA[index] - rowB[index];
}
return rowA[index] > rowB[index] ? 1 : -1;
}
}
// 降序排序
return function(rowA, rowB) {
if (rowA[index] === rowB[index]) {
return 0;
}
if (typeof rowA[index] === 'number') {
return rowB[index] - rowA[index];
}
return rowA[index] < rowB[index] ? 1 : -1;
}
}
// 通过dict, 将 table id, 进行编号
const hash = {};
function size_dict(d){c=0; for (i in d) ++c; return c}
function dict_get(key) {
if(!(key in hash)){
hash[key] = size_dict(hash);
}
return hash[key];
}
function click(evt) {
const $target = $(evt.target);
const $table = $target.parents('.dataframe');
const index = $target.index();
// const tableIndex = $table.index();
const tableIndex = dict_get($table[0].id);
// step1: 拿到原始数据
if (!tableData[tableIndex]) {
// 数据未解析过
const data = [];
const $bodyTrs = $table.find('tbody tr');
$bodyTrs.each(function(index) {
const $tr = $(this);
const $tds = $tr.find('td');
const row = [];
$tds.each(function() {
const text = $(this).text();
const isNumber = !!numberReg.exec(text);
row.push(isNumber ? parseFloat(text) : text);
});
data.push(row);
});
tableData[tableIndex] = data; // 解析数据,存入到本地
}
const data = tableData[tableIndex];
const prevSortInfo = tableSortInfo[tableIndex];
// step2: 对数据进行排序
const newSortType = prevSortInfo && prevSortInfo.sortBy === index && prevSortInfo.sortType === 'desc' ? 'asc' : 'desc';
data.sort(getSorter(index, newSortType));
tableSortInfo[tableIndex] = { sortBy: index, sortType: newSortType }; // 记录当前排序信息
// step3: 生成表格的新的排序内容
const bodyStrs = convertToTbody(data);
const $body = $table.find('tbody');
// step4: 更新表格
$body.html(bodyStrs)
}
// 调用函数
$body.delegate('.dataframe tr th', 'click', click);
</script>
</body>
</html>
'''
def get_html(bizdate, datasets):
contents, call_scripts, tabs = '', '', ''
for i, data in enumerate(datasets):
contents +='''
<div id="t{i}" class="caption">{title}<br>
{comment} <br>
<input type="text" placeholder="正则表达搜索" id="s{i}">
</div>
'''.format(i=i, **data)
contents += data['table'].to_html(index=False, classes='mytable',table_id='t_id_{i}'.format(i=i))
contents += '<br>'
call_scripts += "$('#s{i}').on('keyup', search('#t_id_{i}'));\n".format(i=i)
tabs += '{i}. <a href="#t{i}">{key}</a>;'.format(i=i, key=data['key'])
_map = {'bizdate': bizdate, 'tabs': tabs, 'calls': call_scripts, 'contents': contents}
return _HTML % _map
# 将多个表格数据写入html 中,表格为pandas dataframe格式
datasets = [
{'table': X01,
'key': '分group汇总',
'title': '附表二: 分group汇总报表',
'comment': "<a>分group,不拆分实验.</a>"
},
{'table': X1,
'key': '分媒体',
'title': '表一: 分媒体报表',
'comment': "<a>分媒体, 不分实验.</a>"
},
{'table': X3,
'key': '分实验',
'title': '表三: 分实验报表',
'comment': "<a>不分媒体, 只按照实验id分.</a>"
},
]
c = get_html(bizdate, datasets)
ff = '{}_{}.html'.format(node_name, str(bizdate)[-4:]) # 存最近一个年的.
with open(ff, 'w') as fp:
fp.write(c)
--- 她说, 她是仙,她不是神