Add debug page for trip objects
Add /trip/<start>/debug endpoint that displays the complete trip object data in pretty-printed JSON format with syntax highlighting. Includes both raw data and computed properties for debugging purposes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f396d8a62f
commit
0e2c95117c
128
templates/trip_debug.html
Normal file
128
templates/trip_debug.html
Normal file
|
@ -0,0 +1,128 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Debug: {{ trip.title }} ({{ trip.start }}) - Edward Betts{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<style>
|
||||
.json-display {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.375rem;
|
||||
padding: 1rem;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
overflow-x: auto;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.debug-header {
|
||||
background-color: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 0.375rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.debug-header h1 {
|
||||
color: #856404;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.debug-header p {
|
||||
color: #856404;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.debug-header .btn {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* Basic JSON syntax highlighting using CSS */
|
||||
.json-display .json-key {
|
||||
color: #d73a49;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.json-display .json-string {
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.json-display .json-number {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.json-display .json-boolean {
|
||||
color: #e36209;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.json-display .json-null {
|
||||
color: #6f42c1;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="debug-header">
|
||||
<h1>🐛 Trip Debug Information</h1>
|
||||
<p>Raw trip object data for: <strong>{{ trip.title }}</strong></p>
|
||||
<a href="{{ url_for('trip_page', start=start) }}" class="btn btn-primary">← Back to Trip Page</a>
|
||||
<button onclick="copyToClipboard()" class="btn btn-secondary">📋 Copy JSON</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h3>Trip Object (JSON)</h3>
|
||||
<div class="json-display" id="jsonDisplay">{{ trip_json }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function copyToClipboard() {
|
||||
const jsonText = document.getElementById('jsonDisplay').textContent;
|
||||
navigator.clipboard.writeText(jsonText).then(function() {
|
||||
// Show a temporary notification
|
||||
const btn = event.target;
|
||||
const originalText = btn.textContent;
|
||||
btn.textContent = '✅ Copied!';
|
||||
btn.classList.remove('btn-secondary');
|
||||
btn.classList.add('btn-success');
|
||||
|
||||
setTimeout(function() {
|
||||
btn.textContent = originalText;
|
||||
btn.classList.remove('btn-success');
|
||||
btn.classList.add('btn-secondary');
|
||||
}, 2000);
|
||||
}).catch(function(err) {
|
||||
console.error('Failed to copy: ', err);
|
||||
alert('Failed to copy to clipboard');
|
||||
});
|
||||
}
|
||||
|
||||
// Simple JSON syntax highlighting
|
||||
function highlightJSON() {
|
||||
const display = document.getElementById('jsonDisplay');
|
||||
let content = display.textContent;
|
||||
|
||||
// Highlight different JSON elements
|
||||
content = content.replace(/"([^"]+)":/g, '<span class="json-key">"$1":</span>');
|
||||
content = content.replace(/"([^"]*)"(?=\s*[,\]\}])/g, '<span class="json-string">"$1"</span>');
|
||||
content = content.replace(/\b(\d+\.?\d*)\b/g, '<span class="json-number">$1</span>');
|
||||
content = content.replace(/\b(true|false)\b/g, '<span class="json-boolean">$1</span>');
|
||||
content = content.replace(/\bnull\b/g, '<span class="json-null">null</span>');
|
||||
|
||||
display.innerHTML = content;
|
||||
}
|
||||
|
||||
// Apply highlighting when page loads
|
||||
document.addEventListener('DOMContentLoaded', highlightJSON);
|
||||
</script>
|
||||
{% endblock %}
|
58
web_view.py
58
web_view.py
|
@ -4,8 +4,10 @@
|
|||
|
||||
import decimal
|
||||
import inspect
|
||||
import json
|
||||
import operator
|
||||
import os.path
|
||||
import pprint
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
@ -586,6 +588,62 @@ def trip_page(start: str) -> str:
|
|||
)
|
||||
|
||||
|
||||
@app.route("/trip/<start>/debug")
|
||||
def trip_debug_page(start: str) -> str:
|
||||
"""Trip debug page showing raw trip object data."""
|
||||
route_distances = agenda.travel.load_route_distances(app.config["DATA_DIR"])
|
||||
trip_list = get_trip_list(route_distances)
|
||||
|
||||
prev_trip, trip, next_trip = get_prev_current_and_next_trip(start, trip_list)
|
||||
if not trip:
|
||||
flask.abort(404)
|
||||
|
||||
# Add Schengen compliance information
|
||||
trip = agenda.trip_schengen.add_schengen_compliance_to_trip(trip)
|
||||
|
||||
# Convert trip object to dictionary for display
|
||||
trip_dict = {
|
||||
"start": trip.start.isoformat(),
|
||||
"name": trip.name,
|
||||
"private": trip.private,
|
||||
"travel": trip.travel,
|
||||
"accommodation": trip.accommodation,
|
||||
"conferences": trip.conferences,
|
||||
"events": trip.events,
|
||||
"flight_bookings": trip.flight_bookings,
|
||||
"computed_properties": {
|
||||
"title": trip.title,
|
||||
"end": trip.end.isoformat() if trip.end else None,
|
||||
"countries": [{"name": c.name, "alpha_2": c.alpha_2, "flag": c.flag} for c in trip.countries],
|
||||
"locations": [{"location": loc, "country": {"name": country.name, "alpha_2": country.alpha_2}} for loc, country in trip.locations()],
|
||||
"total_distance": trip.total_distance(),
|
||||
"total_co2_kg": trip.total_co2_kg(),
|
||||
"distances_by_transport_type": trip.distances_by_transport_type(),
|
||||
"co2_by_transport_type": trip.co2_by_transport_type(),
|
||||
},
|
||||
"schengen_compliance": {
|
||||
"total_days_used": trip.schengen_compliance.total_days_used,
|
||||
"days_remaining": trip.schengen_compliance.days_remaining,
|
||||
"is_compliant": trip.schengen_compliance.is_compliant,
|
||||
"current_180_day_period": [
|
||||
trip.schengen_compliance.current_180_day_period[0].isoformat(),
|
||||
trip.schengen_compliance.current_180_day_period[1].isoformat()
|
||||
],
|
||||
"days_over_limit": trip.schengen_compliance.days_over_limit,
|
||||
} if trip.schengen_compliance else None,
|
||||
}
|
||||
|
||||
# Convert to JSON for pretty printing
|
||||
trip_json = json.dumps(trip_dict, indent=2, default=str)
|
||||
|
||||
return flask.render_template(
|
||||
"trip_debug.html",
|
||||
trip=trip,
|
||||
trip_json=trip_json,
|
||||
start=start,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/holidays")
|
||||
def holiday_list() -> str:
|
||||
"""List of holidays."""
|
||||
|
|
Loading…
Reference in a new issue