139 lines
4.7 KiB
Python
139 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List
|
|
|
|
|
|
from cvttpy_tools.app import App
|
|
from cvttpy_tools.base import NamedObject
|
|
from cvttpy_tools.config import CvttAppConfig
|
|
|
|
|
|
class HtmlRenderer(NamedObject):
|
|
def __init__(self) -> None:
|
|
pass
|
|
|
|
@staticmethod
|
|
def render_data_quality(quality: List[Dict[str, Any]]) -> str:
|
|
rows = "".join(
|
|
f"<tr>"
|
|
f"<td>{q.get('instrument','')}</td>"
|
|
f"<td>{q.get('record_count','')}</td>"
|
|
f"<td>{q.get('latest_tstamp','')}</td>"
|
|
f"<td>{q.get('status','')}</td>"
|
|
f"<td>{q.get('reason','')}</td>"
|
|
f"</tr>"
|
|
for q in sorted(quality, key=lambda x: str(x.get("instrument", "")))
|
|
)
|
|
return f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset='utf-8'/>
|
|
<title>Data Quality</title>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; margin: 20px; }}
|
|
table {{ border-collapse: collapse; width: 100%; }}
|
|
th, td {{ border: 1px solid #ccc; padding: 8px; text-align: left; }}
|
|
th {{ background: #f2f2f2; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h2>Data Quality</h2>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Instrument</th><th>Records</th><th>Latest</th><th>Status</th><th>Reason</th></tr>
|
|
</thead>
|
|
<tbody>{rows}</tbody>
|
|
</table>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
@staticmethod
|
|
def render_pairs(pairs: Dict[str, Dict[str, Any]]) -> str:
|
|
if not pairs:
|
|
body = "<p>No pairs available. Check data quality and try again.</p>"
|
|
else:
|
|
body_rows = []
|
|
for pair_name, p in pairs.items():
|
|
body_rows.append(
|
|
"<tr>"
|
|
f"<td>{pair_name}</td>"
|
|
f"<td data-value='{p.get('rank_eg',0)}'>{p.get('rank_eg','')}</td>"
|
|
f"<td data-value='{p.get('rank_adf',0)}'>{p.get('rank_adf','')}</td>"
|
|
f"<td data-value='{p.get('rank_j',0)}'>{p.get('rank_j','')}</td>"
|
|
f"<td data-value='{p.get('pvalue_eg','')}'>{p.get('pvalue_eg','')}</td>"
|
|
f"<td data-value='{p.get('pvalue_adf','')}'>{p.get('pvalue_adf','')}</td>"
|
|
f"<td data-value='{p.get('pvalue_j','')}'>{p.get('pvalue_j','')}</td>"
|
|
"</tr>"
|
|
)
|
|
body = "\n".join(body_rows)
|
|
|
|
return f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset='utf-8'/>
|
|
<title>Pair Selection</title>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; margin: 20px; }}
|
|
table {{ border-collapse: collapse; width: 100%; }}
|
|
th, td {{ border: 1px solid #ccc; padding: 8px; text-align: left; }}
|
|
th.sortable {{ cursor: pointer; background: #f2f2f2; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h2>Pair Selection</h2>
|
|
<table id="pairs-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Pair</th>
|
|
<th class="sortable" data-type="num">Rank-EG</th>
|
|
<th class="sortable" data-type="num">Rank-ADF</th>
|
|
<th class="sortable" data-type="num">Rank-J</th>
|
|
<th>EG p-value</th>
|
|
<th>ADF p-value</th>
|
|
<th>Johansen pseudo p</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{body}
|
|
</tbody>
|
|
</table>
|
|
<script>
|
|
(function() {{
|
|
const table = document.getElementById('pairs-table');
|
|
if (!table) return;
|
|
const getValue = (cell) => {{
|
|
const val = cell.getAttribute('data-value');
|
|
const num = parseFloat(val);
|
|
return isNaN(num) ? val : num;
|
|
}};
|
|
const toggleSort = (index, isNumeric) => {{
|
|
const tbody = table.querySelector('tbody');
|
|
const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
const th = table.querySelectorAll('th')[index];
|
|
const dir = th.getAttribute('data-dir') === 'asc' ? 'desc' : 'asc';
|
|
th.setAttribute('data-dir', dir);
|
|
rows.sort((a, b) => {{
|
|
const va = getValue(a.children[index]);
|
|
const vb = getValue(b.children[index]);
|
|
if (isNumeric && !isNaN(va) && !isNaN(vb)) {{
|
|
return dir === 'asc' ? va - vb : vb - va;
|
|
}}
|
|
return dir === 'asc'
|
|
? String(va).localeCompare(String(vb))
|
|
: String(vb).localeCompare(String(va));
|
|
}});
|
|
tbody.innerHTML = '';
|
|
rows.forEach(r => tbody.appendChild(r));
|
|
}};
|
|
table.querySelectorAll('th.sortable').forEach((th, idx) => {{
|
|
th.addEventListener('click', () => toggleSort(idx, th.dataset.type === 'num'));
|
|
}});
|
|
}})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|