140 lines
No EOL
4.7 KiB
Python
Executable file
140 lines
No EOL
4.7 KiB
Python
Executable file
import json
|
|
from datetime import datetime
|
|
|
|
def generate_html_report(json_data, output_file):
|
|
# Extract input filename from first check
|
|
input_file_path = json_data['checks'][0]['config']['input_file']
|
|
input_filename = input_file_path.split('/')[-1] # Get just the filename
|
|
|
|
# HTML template with Bootstrap for styling
|
|
html_template = f'''
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>QC Report - {input_filename}</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<style>
|
|
.status-badge {{
|
|
font-size: 0.8rem;
|
|
padding: 0.35em 0.65em;
|
|
}}
|
|
.check-card {{
|
|
margin-bottom: 1rem;
|
|
}}
|
|
.details-list {{
|
|
list-style-type: none;
|
|
padding-left: 1.5rem;
|
|
}}
|
|
.details-list li {{
|
|
margin-bottom: 0.5rem;
|
|
}}
|
|
.nested-details {{
|
|
padding-left: 1.5rem;
|
|
margin-top: 0.5rem;
|
|
border-left: 2px solid #dee2e6;
|
|
}}
|
|
.error-section {{
|
|
background-color: #fff3cd;
|
|
border-radius: 4px;
|
|
padding: 1rem;
|
|
margin: 1rem 0;
|
|
}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container py-4">
|
|
<header class="mb-4">
|
|
<h1 class="display-4">QC Report: {input_filename}</h1>
|
|
<p class="text-muted">Generated at: {datetime.fromisoformat(json_data["timestamp"]).strftime('%Y-%m-%d %H:%M:%S')}</p>
|
|
</header>
|
|
|
|
<div class="accordion" id="checksAccordion">
|
|
{''.join([generate_check_html(check) for check in json_data['checks']])}
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
</body>
|
|
</html>
|
|
'''
|
|
|
|
with open(output_file, 'w') as f:
|
|
f.write(html_template)
|
|
|
|
def generate_check_html(check):
|
|
status_color = {
|
|
'passed': 'success',
|
|
'error': 'danger',
|
|
'failed': 'warning'
|
|
}.get(check['result']['status'].lower(), 'secondary')
|
|
|
|
details_html = format_details(check['result'].get('details', {}))
|
|
|
|
error_html = ''
|
|
if 'error_message' in check['result']:
|
|
error_html = f'''
|
|
<div class="error-section">
|
|
<h5 class="text-danger">Error:</h5>
|
|
<p>{check['result']['error_message']}</p>
|
|
</div>
|
|
'''
|
|
|
|
return f'''
|
|
<div class="accordion-item check-card">
|
|
<h2 class="accordion-header" id="heading{check['index']}">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
|
data-bs-target="#collapse{check['index']}" aria-expanded="false"
|
|
aria-controls="collapse{check['index']}">
|
|
<span class="badge bg-{status_color} status-badge me-2">{check['result']['status'].upper()}</span>
|
|
{check['id']}: {check['config']['description']}
|
|
</button>
|
|
</h2>
|
|
<div id="collapse{check['index']}" class="accordion-collapse collapse"
|
|
aria-labelledby="heading{check['index']}" data-bs-parent="#checksAccordion">
|
|
<div class="accordion-body">
|
|
{error_html}
|
|
<h5>Configuration</h5>
|
|
<ul class="details-list">
|
|
{format_details(check['config'])}
|
|
</ul>
|
|
<h5>Results</h5>
|
|
<ul class="details-list">
|
|
{details_html}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
'''
|
|
|
|
def format_details(details, level=0):
|
|
items = []
|
|
for key, value in details.items():
|
|
if isinstance(value, dict):
|
|
items.append(f'''
|
|
<li>
|
|
<strong>{key.title()}:</strong>
|
|
<div class="nested-details">
|
|
{format_details(value, level+1)}
|
|
</div>
|
|
</li>
|
|
''')
|
|
elif isinstance(value, list):
|
|
list_items = ''.join([f'<li>{item}</li>' for item in value])
|
|
items.append(f'''
|
|
<li>
|
|
<strong>{key.title()}:</strong>
|
|
<ul>{list_items}</ul>
|
|
</li>
|
|
''')
|
|
else:
|
|
items.append(f'<li><strong>{key.title()}:</strong> {value}</li>')
|
|
return '\n'.join(items)
|
|
|
|
# Example usage
|
|
if __name__ == "__main__":
|
|
with open('input_report.json') as f:
|
|
data = json.load(f)
|
|
|
|
generate_html_report(data, 'qc_report.html') |