Skip to content

Commit 46102ba

Browse files
committed
eli-420 improving format and layout of reports
1 parent f97c7ce commit 46102ba

File tree

1 file changed

+134
-69
lines changed

1 file changed

+134
-69
lines changed

scripts/generate_dashboard_report.py

Lines changed: 134 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,35 @@
77
from datetime import datetime
88
from pathlib import Path
99

10+
# Context definitions for widgets
11+
WIDGET_CONTEXT = {
12+
"DynamoDB": "Shows the consumed write capacity units for the DynamoDB table. High usage may indicate need for scaling.",
13+
"Lambda": "Number of times the Lambda function was invoked. Spikes may indicate increased traffic or retries.",
14+
"5xx": "Server-side errors. Should be zero ideally.",
15+
"4xx": "Client-side errors. Frequent 4xx errors might indicate issues with client requests.",
16+
"Latency": "Response time of the service. Lower is better.",
17+
"CPU": "CPU utilization percentage. Consistently high CPU might require instance upsizing.",
18+
"Memory": "Memory usage. Ensure there is sufficient headroom.",
19+
"Errors": "Count of error events.",
20+
"Throttles": "Number of throttled requests. Indicates capacity limits are being hit."
21+
}
22+
23+
def get_widget_description(title):
24+
"""
25+
Get a description for a widget based on keywords in its title.
26+
"""
27+
title_lower = title.lower()
28+
description_parts = []
29+
30+
for key, desc in WIDGET_CONTEXT.items():
31+
if key.lower() in title_lower:
32+
description_parts.append(desc)
33+
34+
if not description_parts:
35+
return "Performance metric visualization."
36+
37+
return " ".join(description_parts)
38+
1039
def generate_html_report(images_dir='dashboard_exports', output_file=None):
1140
"""
1241
Generate an HTML report with all dashboard widget images.
@@ -35,127 +64,156 @@ def generate_html_report(images_dir='dashboard_exports', output_file=None):
3564

3665
# Get dashboard name and timestamp from definition file
3766
dashboard_name = "Monthly Demand And Capacity Report - EliD"
38-
report_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
67+
report_date = datetime.now().strftime('%d %B %Y at %H:%M')
3968

4069
# Build HTML
4170
html_content = f"""<!DOCTYPE html>
4271
<html lang="en">
4372
<head>
4473
<meta charset="UTF-8">
4574
<meta name="viewport" content="width=device-width, initial-scale=1.0">
46-
<title>Dashboard Report - {dashboard_name}</title>
75+
<title>NHS Dashboard Report - {dashboard_name}</title>
4776
<style>
77+
:root {{
78+
--nhs-blue: #005EB8;
79+
--nhs-white: #FFFFFF;
80+
--nhs-black: #231f20;
81+
--nhs-dark-grey: #425563;
82+
--nhs-mid-grey: #768692;
83+
--nhs-pale-grey: #E8EDEE;
84+
--nhs-warm-yellow: #FFB81C;
85+
}}
86+
4887
* {{
4988
margin: 0;
5089
padding: 0;
5190
box-sizing: border-box;
5291
}}
5392
5493
body {{
55-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
56-
background: #f5f5f5;
57-
padding: 20px;
58-
color: #333;
94+
font-family: "Frutiger W01", Arial, sans-serif;
95+
background: var(--nhs-pale-grey);
96+
color: var(--nhs-black);
97+
line-height: 1.5;
98+
}}
99+
100+
.nhs-header {{
101+
background-color: var(--nhs-blue);
102+
color: var(--nhs-white);
103+
padding: 24px 0;
104+
margin-bottom: 32px;
59105
}}
60106
61-
.container {{
62-
max-width: 1400px;
107+
.nhs-container {{
108+
max-width: 960px;
63109
margin: 0 auto;
64-
background: white;
65-
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
110+
padding: 0 16px;
66111
}}
67112
68-
.header {{
69-
background: linear-gradient(135deg, #232F3E 0%, #FF9900 100%);
70-
color: white;
71-
padding: 30px 40px;
72-
border-bottom: 4px solid #FF9900;
113+
.nhs-logo {{
114+
font-weight: 700;
115+
font-size: 24px;
116+
letter-spacing: -0.5px;
117+
display: inline-block;
118+
margin-right: 16px;
119+
padding-right: 16px;
120+
border-right: 1px solid rgba(255, 255, 255, 0.3);
73121
}}
74122
75-
.header h1 {{
76-
font-size: 32px;
123+
.report-title {{
124+
font-size: 24px;
77125
font-weight: 600;
78-
margin-bottom: 10px;
126+
display: inline-block;
79127
}}
80128
81-
.header .subtitle {{
82-
font-size: 16px;
129+
.report-meta {{
130+
margin-top: 8px;
131+
font-size: 14px;
83132
opacity: 0.9;
84133
}}
85134
86135
.content {{
87-
padding: 40px;
136+
padding-bottom: 48px;
88137
}}
89138
90-
.widget {{
91-
margin-bottom: 40px;
139+
.widget-card {{
140+
background: var(--nhs-white);
141+
border: 1px solid #d8dde0;
142+
border-bottom: 4px solid var(--nhs-blue);
143+
margin-bottom: 32px;
144+
padding: 24px;
92145
page-break-inside: avoid;
93146
}}
94147
148+
.widget-header {{
149+
margin-bottom: 16px;
150+
border-bottom: 1px solid var(--nhs-pale-grey);
151+
padding-bottom: 16px;
152+
}}
153+
95154
.widget-title {{
96-
font-size: 20px;
155+
font-size: 19px;
97156
font-weight: 600;
98-
color: #232F3E;
99-
margin-bottom: 15px;
100-
padding-bottom: 10px;
101-
border-bottom: 2px solid #FF9900;
157+
color: var(--nhs-black);
158+
margin-bottom: 8px;
159+
}}
160+
161+
.widget-description {{
162+
font-size: 16px;
163+
color: var(--nhs-dark-grey);
164+
background: #f0f4f5;
165+
padding: 12px;
166+
border-left: 4px solid var(--nhs-mid-grey);
167+
}}
168+
169+
.widget-image-container {{
170+
margin-top: 20px;
171+
text-align: center;
102172
}}
103173
104174
.widget-image {{
105-
width: 100%;
175+
max-width: 100%;
106176
height: auto;
107-
border: 1px solid #ddd;
108-
border-radius: 4px;
109-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
177+
border: 1px solid var(--nhs-pale-grey);
110178
}}
111179
112180
.footer {{
113-
background: #f8f8f8;
114-
padding: 20px 40px;
115181
text-align: center;
116-
color: #666;
182+
padding: 32px 0;
183+
color: var(--nhs-mid-grey);
117184
font-size: 14px;
118-
border-top: 1px solid #ddd;
185+
border-top: 1px solid #d8dde0;
186+
margin-top: 48px;
119187
}}
120188
121189
@media print {{
122190
body {{
123191
background: white;
124-
padding: 0;
125-
}}
126-
127-
.container {{
128-
box-shadow: none;
129-
}}
130-
131-
.widget {{
132-
page-break-inside: avoid;
133192
}}
134-
}}
135-
136-
@media (max-width: 768px) {{
137-
body {{
138-
padding: 10px;
139-
}}
140-
141-
.content {{
142-
padding: 20px;
193+
.nhs-header {{
194+
background: white;
195+
color: black;
196+
border-bottom: 2px solid var(--nhs-blue);
143197
}}
144-
145-
.header {{
146-
padding: 20px;
198+
.widget-card {{
199+
border: none;
200+
border-bottom: 1px solid #ccc;
147201
}}
148202
}}
149203
</style>
150204
</head>
151205
<body>
152-
<div class="container">
153-
<div class="header">
154-
<h1>📊 {dashboard_name}</h1>
155-
<div class="subtitle">Last 8 Weeks Report • Generated: {report_date}</div>
206+
<header class="nhs-header">
207+
<div class="nhs-container">
208+
<div class="header-content">
209+
<span class="nhs-logo">NHS</span>
210+
<h1 class="report-title">{dashboard_name}</h1>
211+
<div class="report-meta">Generated on {report_date}</div>
212+
</div>
156213
</div>
214+
</header>
157215
158-
<div class="content">
216+
<div class="nhs-container content">
159217
"""
160218

161219
# Add each widget image
@@ -167,27 +225,35 @@ def generate_html_report(images_dir='dashboard_exports', output_file=None):
167225
# Replace underscores with spaces
168226
title = title.replace('_', ' ')
169227

228+
description = get_widget_description(title)
229+
170230
# Read and encode image
171231
with open(image_file, 'rb') as f:
172232
image_data = base64.b64encode(f.read()).decode('utf-8')
173233

174234
html_content += f"""
175-
<div class="widget">
235+
<div class="widget-card">
236+
<div class="widget-header">
176237
<div class="widget-title">{idx}. {title}</div>
238+
<div class="widget-description">{description}</div>
239+
</div>
240+
<div class="widget-image-container">
177241
<img class="widget-image"
178242
src="data:image/png;base64,{image_data}"
179243
alt="{title}">
180244
</div>
245+
</div>
181246
"""
182247

183248
# Close HTML
184249
html_content += """
185-
</div>
250+
</div>
186251
187-
<div class="footer">
188-
Generated from AWS CloudWatch Dashboard
252+
<footer class="footer">
253+
<div class="nhs-container">
254+
<p>Eligibility Data Product • Generated from AWS CloudWatch</p>
189255
</div>
190-
</div>
256+
</footer>
191257
</body>
192258
</html>
193259
"""
@@ -196,11 +262,10 @@ def generate_html_report(images_dir='dashboard_exports', output_file=None):
196262
with open(output_file, 'w', encoding='utf-8') as f:
197263
f.write(html_content)
198264

199-
print(f"\n✓ Report generated: {output_file}")
265+
print(f"\nCapacity and Demand Report generated: {output_file}")
200266
print(f"\nTo view:")
201267
print(f" - Open in browser: file://{Path(output_file).absolute()}")
202268
print(f" - Or run: xdg-open {output_file}")
203-
print(f"\nTo save as PDF: Open in browser → Print → Save as PDF")
204269

205270
return output_file
206271

0 commit comments

Comments
 (0)