organize the recent checks
This commit is contained in:
18
app/main.py
18
app/main.py
@@ -140,10 +140,22 @@ def report(service_id):
|
|||||||
from_ts, to_ts, from_display, to_display = _parse_report_dates(from_ts, to_ts, preset)
|
from_ts, to_ts, from_display, to_display = _parse_report_dates(from_ts, to_ts, preset)
|
||||||
status_filter = request.args.get("status")
|
status_filter = request.args.get("status")
|
||||||
search = request.args.get("search", "").strip() or None
|
search = request.args.get("search", "").strip() or None
|
||||||
|
page = max(1, int(request.args.get("page", 1)))
|
||||||
|
per_page = min(100, max(10, int(request.args.get("per_page", 25))))
|
||||||
stats = models.get_report_stats(service_id, from_ts=from_ts, to_ts=to_ts)
|
stats = models.get_report_stats(service_id, from_ts=from_ts, to_ts=to_ts)
|
||||||
checks = models.get_checks(service_id, limit=100, from_ts=from_ts, to_ts=to_ts, status_filter=status_filter, search=search)
|
checks_total = models.get_checks_count(service_id, from_ts=from_ts, to_ts=to_ts, status_filter=status_filter, search=search)
|
||||||
|
checks = models.get_checks(
|
||||||
|
service_id,
|
||||||
|
limit=per_page,
|
||||||
|
offset=(page - 1) * per_page,
|
||||||
|
from_ts=from_ts,
|
||||||
|
to_ts=to_ts,
|
||||||
|
status_filter=status_filter,
|
||||||
|
search=search,
|
||||||
|
)
|
||||||
chart_checks = models.get_checks(service_id, limit=200, from_ts=from_ts, to_ts=to_ts)
|
chart_checks = models.get_checks(service_id, limit=200, from_ts=from_ts, to_ts=to_ts)
|
||||||
period_label = _format_period_label(from_display, to_display) if (from_ts or to_ts) else None
|
period_label = _format_period_label(from_display, to_display) if (from_ts or to_ts) else None
|
||||||
|
total_pages = (checks_total + per_page - 1) // per_page if checks_total else 1
|
||||||
return render_template(
|
return render_template(
|
||||||
"report.html",
|
"report.html",
|
||||||
service=dict(svc),
|
service=dict(svc),
|
||||||
@@ -157,6 +169,10 @@ def report(service_id):
|
|||||||
preset=preset,
|
preset=preset,
|
||||||
status_filter=status_filter or "",
|
status_filter=status_filter or "",
|
||||||
search=search or "",
|
search=search or "",
|
||||||
|
page=page,
|
||||||
|
per_page=per_page,
|
||||||
|
checks_total=checks_total,
|
||||||
|
total_pages=total_pages,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -137,10 +137,9 @@ def add_check(service_id: int, success: bool, response_time_ms: float | None, er
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_checks(service_id: int, limit: int = 50, from_ts: str = None, to_ts: str = None, status_filter: str = None, search: str = None):
|
def _checks_where_args(service_id: int, from_ts: str = None, to_ts: str = None, status_filter: str = None, search: str = None):
|
||||||
"""Get recent checks for a service, optionally filtered by timestamp, status (ok/error), and error search."""
|
"""Build WHERE clause and args for checks queries."""
|
||||||
with get_db() as conn:
|
q = "WHERE service_id = ?"
|
||||||
q = "SELECT * FROM checks WHERE service_id = ?"
|
|
||||||
args = [service_id]
|
args = [service_id]
|
||||||
if from_ts:
|
if from_ts:
|
||||||
q += " AND timestamp >= ?"
|
q += " AND timestamp >= ?"
|
||||||
@@ -155,9 +154,23 @@ def get_checks(service_id: int, limit: int = 50, from_ts: str = None, to_ts: str
|
|||||||
if search:
|
if search:
|
||||||
q += " AND (error_message LIKE ? OR status LIKE ?)"
|
q += " AND (error_message LIKE ? OR status LIKE ?)"
|
||||||
args.extend([f"%{search}%", f"%{search}%"])
|
args.extend([f"%{search}%", f"%{search}%"])
|
||||||
q += " ORDER BY timestamp DESC LIMIT ?"
|
return q, args
|
||||||
args.append(limit)
|
|
||||||
rows = conn.execute(q, args).fetchall()
|
|
||||||
|
def get_checks_count(service_id: int, from_ts: str = None, to_ts: str = None, status_filter: str = None, search: str = None) -> int:
|
||||||
|
"""Count checks matching filters (for pagination)."""
|
||||||
|
where, args = _checks_where_args(service_id, from_ts, to_ts, status_filter, search)
|
||||||
|
with get_db() as conn:
|
||||||
|
row = conn.execute(f"SELECT COUNT(*) FROM checks {where}", args).fetchone()
|
||||||
|
return row[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_checks(service_id: int, limit: int = 50, offset: int = 0, from_ts: str = None, to_ts: str = None, status_filter: str = None, search: str = None):
|
||||||
|
"""Get recent checks for a service, optionally filtered and paginated."""
|
||||||
|
where, args = _checks_where_args(service_id, from_ts, to_ts, status_filter, search)
|
||||||
|
args.extend([limit, offset])
|
||||||
|
with get_db() as conn:
|
||||||
|
rows = conn.execute(f"SELECT * FROM checks {where} ORDER BY timestamp DESC LIMIT ? OFFSET ?", args).fetchall()
|
||||||
return [dict(r) for r in rows]
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -254,6 +254,55 @@ h2 {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-info {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-links {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.35rem 0.6rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
background: var(--surface);
|
||||||
|
color: var(--text);
|
||||||
|
border: 1px solid var(--muted);
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-btn.pagination-current {
|
||||||
|
background: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-ellipsis {
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-delete {
|
.btn-delete {
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
<p class="period-label">Showing: {{ period_label }}</p>
|
<p class="period-label">Showing: {{ period_label }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="get" action="{{ url_for('report', service_id=service.id) }}" class="date-range-form">
|
<form method="get" action="{{ url_for('report', service_id=service.id) }}" class="date-range-form">
|
||||||
|
<input type="hidden" name="preset" value="{{ preset or '' }}">
|
||||||
|
<input type="hidden" name="status" value="{{ status_filter or '' }}">
|
||||||
|
<input type="hidden" name="search" value="{{ search or '' }}">
|
||||||
|
<input type="hidden" name="per_page" value="{{ per_page }}">
|
||||||
<label>From</label>
|
<label>From</label>
|
||||||
<input type="datetime-local" name="from" value="{{ from_date }}" placeholder="Start (optional)">
|
<input type="datetime-local" name="from" value="{{ from_date }}" placeholder="Start (optional)">
|
||||||
<label>To</label>
|
<label>To</label>
|
||||||
@@ -71,12 +75,19 @@
|
|||||||
<input type="hidden" name="preset" value="{{ preset or '' }}">
|
<input type="hidden" name="preset" value="{{ preset or '' }}">
|
||||||
<input type="hidden" name="from" value="{{ from_date }}">
|
<input type="hidden" name="from" value="{{ from_date }}">
|
||||||
<input type="hidden" name="to" value="{{ to_date }}">
|
<input type="hidden" name="to" value="{{ to_date }}">
|
||||||
|
<input type="hidden" name="page" value="1">
|
||||||
<select name="status">
|
<select name="status">
|
||||||
<option value="">All</option>
|
<option value="">All</option>
|
||||||
<option value="ok" {% if status_filter == 'ok' %}selected{% endif %}>OK only</option>
|
<option value="ok" {% if status_filter == 'ok' %}selected{% endif %}>OK only</option>
|
||||||
<option value="error" {% if status_filter == 'error' %}selected{% endif %}>Errors only</option>
|
<option value="error" {% if status_filter == 'error' %}selected{% endif %}>Errors only</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" name="search" value="{{ search }}" placeholder="Search error message...">
|
<input type="text" name="search" value="{{ search }}" placeholder="Search error message...">
|
||||||
|
<select name="per_page">
|
||||||
|
<option value="10" {% if per_page == 10 %}selected{% endif %}>10 per page</option>
|
||||||
|
<option value="25" {% if per_page == 25 %}selected{% endif %}>25 per page</option>
|
||||||
|
<option value="50" {% if per_page == 50 %}selected{% endif %}>50 per page</option>
|
||||||
|
<option value="100" {% if per_page == 100 %}selected{% endif %}>100 per page</option>
|
||||||
|
</select>
|
||||||
<button type="submit">Filter</button>
|
<button type="submit">Filter</button>
|
||||||
</form>
|
</form>
|
||||||
<table class="checks-table">
|
<table class="checks-table">
|
||||||
@@ -110,6 +121,45 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{% if checks_total > 0 %}
|
||||||
|
<nav class="pagination">
|
||||||
|
<span class="pagination-info">
|
||||||
|
Showing {{ (page - 1) * per_page + 1 }}-{{ min(page * per_page, checks_total) }} of {{ checks_total }}
|
||||||
|
</span>
|
||||||
|
<div class="pagination-links">
|
||||||
|
{% if page > 1 %}
|
||||||
|
<a href="{{ url_for('report', service_id=service.id, preset=preset or '', from=from_date, to=to_date, status=status_filter or '', search=search or '', per_page=per_page, page=page-1) }}" class="pagination-btn">Previous</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if total_pages <= 7 %}
|
||||||
|
{% for p in range(1, total_pages + 1) %}
|
||||||
|
{% if p == page %}
|
||||||
|
<span class="pagination-btn pagination-current">{{ p }}</span>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('report', service_id=service.id, preset=preset or '', from=from_date, to=to_date, status=status_filter or '', search=search or '', per_page=per_page, page=p) }}" class="pagination-btn">{{ p }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('report', service_id=service.id, preset=preset or '', from=from_date, to=to_date, status=status_filter or '', search=search or '', per_page=per_page, page=1) }}" class="pagination-btn">1</a>
|
||||||
|
{% if page > 3 %}<span class="pagination-ellipsis">…</span>{% endif %}
|
||||||
|
{% for p in range(max(2, page - 1), min(total_pages, page + 1) + 1) %}
|
||||||
|
{% if p == page %}
|
||||||
|
<span class="pagination-btn pagination-current">{{ p }}</span>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('report', service_id=service.id, preset=preset or '', from=from_date, to=to_date, status=status_filter or '', search=search or '', per_page=per_page, page=p) }}" class="pagination-btn">{{ p }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if page < total_pages - 2 %}<span class="pagination-ellipsis">…</span>{% endif %}
|
||||||
|
{% if total_pages > 1 %}
|
||||||
|
<a href="{{ url_for('report', service_id=service.id, preset=preset or '', from=from_date, to=to_date, status=status_filter or '', search=search or '', per_page=per_page, page=total_pages) }}" class="pagination-btn">{{ total_pages }}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if page < total_pages %}
|
||||||
|
<a href="{{ url_for('report', service_id=service.id, preset=preset or '', from=from_date, to=to_date, status=status_filter or '', search=search or '', per_page=per_page, page=page+1) }}" class="pagination-btn">Next</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="/">← Back to Dashboard</a>
|
<a href="/">← Back to Dashboard</a>
|
||||||
<span style="margin-left: 1rem;">
|
<span style="margin-left: 1rem;">
|
||||||
|
|||||||
Reference in New Issue
Block a user