Skip to content

Commit 2eb98f3

Browse files
Merge pull request #15 from shadab-mohammad-oracle/patch-5
Create index.html
2 parents df611c9 + 2d0c03a commit 2eb98f3

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed

app/templates/index.html

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>DB Endpoint Latency Checker</title>
6+
<link rel="stylesheet" href="/static/style.css"/>
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
8+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9+
<style>
10+
.centered-block { margin: 0 auto; display: flex; flex-direction: column; align-items: center; }
11+
.summary-table { border-collapse: collapse; margin: 1.2em auto 0 auto; font-size:1.07rem; border:1.3px solid #dde1ec; }
12+
.summary-table th, .summary-table td { padding: 0.62em 1.2em; text-align: left; border-bottom:1px solid #ececec;}
13+
.summary-table th { font-weight: 700; }
14+
.summary-table tr:last-child td { border-bottom: none; }
15+
.explanation-block { max-width: 500px; margin: 1.5em auto 0 auto; text-align: left; color: #525763; font-size:0.97rem; }
16+
.latency-table { font-family: "Segoe UI", Arial, "Liberation Sans", "system-ui", sans-serif; font-size:1.09rem; border-collapse: collapse; border:1.3px solid #dde1ec;}
17+
.latency-table th, .latency-table td { border:1px solid #dde1ec; padding:0.56em 0.9em;}
18+
.summary-label { font-weight:700; }
19+
.summary-value { font-weight:400; }
20+
</style>
21+
</head>
22+
<body>
23+
<main>
24+
<form class="minimal-form" method="post" action="/test-latency" id="latency-form" autocomplete="off" onsubmit="runProgressBar()">
25+
<h2 class="minimal-title">DB Endpoint Latency Checker</h2>
26+
<div class="minimal-group">
27+
<label for="dbtype">Database Type</label>
28+
<select name="dbtype" id="dbtype" required>
29+
<option value="" disabled selected>Select database...</option>
30+
<option value="oracle" {% if dbtype == 'oracle' %}selected{% endif %}>Oracle</option>
31+
<option value="postgresql" {% if dbtype == 'postgresql' %}selected{% endif %}>PostgreSQL</option>
32+
<option value="mysql" {% if dbtype == 'mysql' %}selected{% endif %}>MySQL</option>
33+
<option value="sqlserver" {% if dbtype == 'sqlserver' %}selected{% endif %}>SQL Server</option>
34+
<option value="url" {% if dbtype == 'url' %}selected{% endif %}>HTTP(S) URL</option>
35+
</select>
36+
</div>
37+
<div id="db-fields" class="minimal-fields" {% if dbtype == "url" %}style="display:none;"{% endif %}>
38+
<div class="minimal-group">
39+
<label for="host">Host / DSN</label>
40+
<input type="text" id="host" name="host" value="{{ host|default('') }}" placeholder="e.g. localhost or DSN string" autocomplete="off">
41+
</div>
42+
<div class="minimal-group">
43+
<label for="port">Port</label>
44+
<input type="text" id="port" name="port" value="{{ port|default('') }}" autocomplete="off">
45+
</div>
46+
<div class="minimal-group">
47+
<label for="username">Username</label>
48+
<input type="text" id="username" name="username" value="{{ username|default('') }}" autocomplete="off" placeholder="Database user">
49+
</div>
50+
<div class="minimal-group">
51+
<label for="password">Password</label>
52+
<input type="password" id="password" name="password" autocomplete="new-password" placeholder="••••••••••••">
53+
</div>
54+
<div class="minimal-group">
55+
<label for="database">Database Name</label>
56+
<input type="text" id="database" name="database" value="{{ database|default('') }}" autocomplete="off">
57+
</div>
58+
</div>
59+
<div id="url-fields" class="minimal-fields" {% if dbtype != "url" %}style="display:none;"{% endif %}>
60+
<div class="minimal-group">
61+
<label for="url">Test URL</label>
62+
<input type="text" id="url" name="url" value="{{ url|default('') }}" placeholder="https://example.com" autocomplete="off">
63+
</div>
64+
</div>
65+
<div class="minimal-group">
66+
<label for="period">Test Duration (seconds)</label>
67+
<input type="number" id="period" name="period" value="{{ period|default(10) }}" min="1">
68+
</div>
69+
<div class="minimal-group">
70+
<label for="interval">Delay Between Tests (seconds)</label>
71+
<input type="number" id="interval" name="interval" value="{{ interval|default(1) }}" min="0.1" step="0.1">
72+
</div>
73+
<footer class="minimal-footer">
74+
<button type="submit" class="minimal-btn">Run Test</button>
75+
<button type="reset" class="minimal-btn minimal-btn-secondary" onclick="formReset(); return false;">Clear</button>
76+
</footer>
77+
<div id="loading-indicator" style="display:none; text-align:center; margin-top:1em;">
78+
<div class="loader"></div>
79+
<div id="prog-label" style="margin:0.8em 0; color:#455;"></div>
80+
<progress id="progress-bar" max="100" value="1" style="width: 90%;"></progress>
81+
</div>
82+
</form>
83+
84+
{% if result %}
85+
<section class="result-card result-section printable centered-block" id="printable-result" style="margin:2em auto 1.2em auto;max-width:650px;">
86+
<h3 style="margin:2em 0 0.65em 0; text-align:center;">Latency Chart (ms over test time)</h3>
87+
<div class="pulse-chart-box" style="margin:0 0 1.1em 0;">
88+
<canvas id="pulseChart" width="580" height="110" style="background:#fafbfc; border-radius:10px;"></canvas>
89+
</div>
90+
<h3 style="margin:1em 0 0.2em 0; text-align:center;">Latency Results</h3>
91+
<div class="table-wrap" style="margin-bottom:1.3em;">
92+
<table class="excel-like latency-table" style="margin:0 auto;">
93+
<thead>
94+
<tr>
95+
<th>#</th>
96+
<th>Timestamp</th>
97+
<th>Latency (ms)</th>
98+
<th>Status</th>
99+
<th>Error</th>
100+
</tr>
101+
</thead>
102+
<tbody>
103+
{% for row in result.details %}
104+
<tr class="{{ 'success' if row.success else 'error' }}">
105+
<td>{{ loop.index }}</td>
106+
<td>{{ row.timestamp }}</td>
107+
<td><span style="font-weight:500;">{{ "%.2f"|format(row.latency_ms) }}</span></td>
108+
<td>{{ 'OK' if row.success else 'Fail' }}</td>
109+
<td>
110+
{% if row.error %}
111+
<span class="errormsg">{{ row.error }}</span>
112+
{% else %} - {% endif %}
113+
</td>
114+
</tr>
115+
{% endfor %}
116+
</tbody>
117+
</table>
118+
</div>
119+
<div style="margin-top:0.6em;"></div>
120+
<h3 style="text-align:center;margin-bottom:0.5em;">Summary of Statistics</h3>
121+
<table class="summary-table">
122+
<tr><th class="summary-label">P99</th><td class="summary-value">{{ result.latency_stats.p99 is not none and "%.2f"|format(result.latency_stats.p99) or 'N/A' }} ms</td></tr>
123+
<tr><th class="summary-label">P90</th><td class="summary-value">{{ result.latency_stats.p90 is not none and "%.2f"|format(result.latency_stats.p90) or 'N/A' }} ms</td></tr>
124+
<tr><th class="summary-label">Average</th><td class="summary-value">{{ result.latency_stats.avg is not none and "%.2f"|format(result.latency_stats.avg) or 'N/A' }} ms</td></tr>
125+
<tr><th class="summary-label">Mean</th><td class="summary-value">{{ result.latency_stats.mean is not none and "%.2f"|format(result.latency_stats.mean) or 'N/A' }} ms</td></tr>
126+
<tr><th class="summary-label">StdDev</th><td class="summary-value">{{ result.latency_stats.stddev is not none and "%.2f"|format(result.latency_stats.stddev) or 'N/A' }} ms</td></tr>
127+
<tr><th class="summary-label">Runs</th><td class="summary-value">{{ result.latency_stats.runs | default('N/A') }}</td></tr>
128+
</table>
129+
<div class="explanation-block">
130+
<b>What these statistics mean:</b><br>
131+
<b>P99</b>: 99% of your latency measurements are lower than this value (i.e., only 1% of requests took longer).<br>
132+
<b>P90</b>: 90% of your latency measurements are lower than this value (i.e., only 10% of requests took longer).<br>
133+
<b>Average</b>: Arithmetic average of all measured latencies.<br>
134+
<b>Mean</b>: The same as the average in this context.<br>
135+
<b>StdDev</b>: Standard deviation, showing how much your latency varied.<br>
136+
<b>Runs</b>: The total number of measurement points.<br>
137+
</div>
138+
</section>
139+
<script>
140+
// Chart draw
141+
const latencies = [{{ result.details | map(attribute='latency_ms') | join(',') }}];
142+
const ctx = document.getElementById('pulseChart').getContext('2d');
143+
new Chart(ctx, {
144+
type: 'line',
145+
data: {
146+
labels: latencies.map((_,i)=>i+1),
147+
datasets: [{
148+
label: 'Latency (ms)',
149+
data: latencies,
150+
fill: false,
151+
borderColor: '#47a992',
152+
backgroundColor: '#3ba7c844',
153+
tension: 0.33,
154+
pointRadius: 2.5,
155+
pointBackgroundColor: '#0b7da1'
156+
}]
157+
},
158+
options: {
159+
responsive: false,
160+
plugins: { legend: { display: false }},
161+
scales: {
162+
x: {
163+
title: {display:true, text: "Test Iteration"},
164+
display: true,
165+
grid: { color: "#ecf0f7"}
166+
},
167+
y: {
168+
beginAtZero: true,
169+
grid: {drawBorder:false, color:'#e0e0fa'},
170+
title: {display:true, text: "Latency (ms)"}
171+
}
172+
}
173+
}
174+
});
175+
</script>
176+
{% endif %}
177+
</main>
178+
<script>
179+
const dbSelect = document.getElementById('dbtype');
180+
const dbFields = document.getElementById('db-fields');
181+
const urlFields = document.getElementById('url-fields');
182+
function toggleDbUi() {
183+
if (dbSelect.value === 'url') {
184+
dbFields.style.display = 'none';
185+
urlFields.style.display = '';
186+
} else {
187+
dbFields.style.display = '';
188+
urlFields.style.display = 'none';
189+
}
190+
}
191+
dbSelect.onchange = toggleDbUi;
192+
window.onload = toggleDbUi;
193+
function formReset() {
194+
document.getElementById('latency-form').reset();
195+
dbFields.style.display = '';
196+
urlFields.style.display = 'none';
197+
}
198+
// Loader/progress for test (progress based on Test Duration)
199+
function runProgressBar() {
200+
const loader = document.getElementById('loading-indicator');
201+
loader.style.display = '';
202+
let prog = document.getElementById('progress-bar');
203+
let label = document.getElementById('prog-label');
204+
let period = parseFloat(document.getElementById('period').value) || 10;
205+
let elapsed = 0;
206+
let intervalMs = 250;
207+
prog.value = 1;
208+
label.innerText = "Starting...";
209+
let progressInterval = setInterval(function(){
210+
if (loader.style.display === "none") {clearInterval(progressInterval); return;}
211+
elapsed += intervalMs/1000;
212+
let percent = Math.min(99, 100 * elapsed / period);
213+
prog.value = percent;
214+
label.innerText = "Test progress: " + Math.floor(percent) + "% ("+Math.round(elapsed)+"s / "+period+"s)";
215+
if (percent >= 99) label.innerText = "Finishing...";
216+
}, intervalMs);
217+
}
218+
</script>
219+
</body>
220+
</html>

0 commit comments

Comments
 (0)