up emoji on demo map

This commit is contained in:
Tykayn 2025-09-27 00:39:18 +02:00 committed by tykayn
parent 65956ff6be
commit dea71fc6b3
7 changed files with 565 additions and 35 deletions

View file

@ -66,7 +66,7 @@ class DemoResource:
template = self.jinja_env.get_template('edit.html')
html = template.render(
id=id,
event_data=json.dumps(event_data)
event_data=event_data
)
# Set the response body and status
@ -404,5 +404,64 @@ class DemoResource:
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
def on_get_property_stats(self, req, resp):
"""
Handle GET requests to the /demo/property-stats endpoint.
Returns an HTML page with statistics about property occurrences in the last 200 events.
Args:
req: The request object.
resp: The response object.
"""
logger.info("Processing GET request to /demo/property-stats")
try:
# Set content type to HTML
resp.content_type = 'text/html'
# Fetch the last 200 events from the API
try:
response = requests.get('https://api.openeventdatabase.org/event?limit=200')
if response.status_code == 200 and response.text:
events_data = response.json()
else:
logger.error(f"Error fetching events: Status code {response.status_code}")
events_data = {"features": []}
except Exception as e:
logger.error(f"Error fetching events: {e}")
events_data = {"features": []}
# Count property occurrences
property_counts = {}
total_events = len(events_data.get('features', []))
for feature in events_data.get('features', []):
properties = feature.get('properties', {})
for prop_name in properties.keys():
if prop_name in property_counts:
property_counts[prop_name] += 1
else:
property_counts[prop_name] = 1
# Sort properties by occurrence count (descending order)
sorted_properties = sorted(property_counts.items(), key=lambda x: x[1], reverse=True)
# Render the template
template = self.jinja_env.get_template('property_stats.html')
html = template.render(
property_stats=sorted_properties,
total_events=total_events,
unique_properties=len(sorted_properties)
)
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /demo/property-stats")
except Exception as e:
logger.error(f"Error processing GET request to /demo/property-stats: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
# Create a global instance of DemoResource
demo = DemoResource()

View file

@ -297,6 +297,7 @@ class DemoMainResource:
<p><a href="/demo/traffic" class="add-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #0078ff; color: white; border-radius: 4px; font-weight: bold;">+ Traffic event</a></p>
<p><a href="/demo/add" class="add-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #0078ff; color: white; border-radius: 4px; font-weight: bold;">+ Any Event</a></p>
<p><a href="/demo/live" class="live-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #0078ff; color: white; border-radius: 4px; font-weight: bold;"> Live</a></p>
<p><a href="/demo/property-stats" class="stats-event-btn" style="display: block; text-align: center; margin-top: 15px; padding: 8px; background-color: #28a745; color: white; border-radius: 4px; font-weight: bold;">📊 Statistiques propriétés</a></p>
@ -304,7 +305,7 @@ class DemoMainResource:
<br/>
<br/>
<!-- Filtres pour les événements -->
<div class="event-filters" id="filters_panel" style="margin-top: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 4px; display:none;">
<div class="event-filters" id="filters_panel" style="margin-top: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 4px;">
<h3 id="filters_header" style="margin-top: 0; color: #0078ff; cursor:pointer;">Filtres</h3>
<div style="margin-top: 10px;">
<label style="display: block; margin-bottom: 5px;">Type d'événement:</label>
@ -317,14 +318,14 @@ class DemoMainResource:
</select>
</div>
<div style="margin-top:12px; display:flex; align-items:center; gap:8px;">
<input type="checkbox" id="autoRefreshToggle" checked>
<label for="autoRefreshToggle" style="margin:0;">Rafraîchissement automatique (30s)</label>
</div>
</div>
<div class="event-filters" style="margin-top: 10px; padding: 10px; background-color: #fff; border: 1px solid #e5e7eb; border-radius: 4px;">
<h3 style="margin-top: 0; color: #0078ff;">Histogramme des évènements</h3>
<div style="margin-top:12px; display:flex; align-items:center; gap:8px;">
<input type="checkbox" id="autoRefreshToggle" checked>
<label for="autoRefreshToggle" style="margin:0;">Rafraîchissement automatique (30s)</label>
</div>
<canvas id="eventsHistogram" style="width:100%; height:220px;"></canvas>
</div>
@ -364,6 +365,28 @@ class DemoMainResource:
criteria: (name, description, what) => {
return (what || '').toLowerCase().includes('bike');
}
},
// Emoji casque de chantier pour les travaux
construction: {
emoji: '⛑️',
criteria: (name, description, what) => {
const text = (name + ' ' + description + ' ' + what).toLowerCase();
return text.includes('travaux');
}
},
// Emoji soleil pour les types contenant "daylight"
daylight: {
emoji: '☀️',
criteria: (name, description, what) => {
return (what || '').toLowerCase().includes('daylight');
}
},
// Emoji carte pour les types contenant "community.osm"
osm_community: {
emoji: '🗺️',
criteria: (name, description, what) => {
return (what || '').toLowerCase().includes('community.osm');
}
}
};

View file

@ -7,6 +7,8 @@
{% endblock %}
{% block head %}
<script src="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@mapbox/mapbox-gl-draw@1.4.3/dist/mapbox-gl-draw.css" type="text/css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
@ -83,19 +85,15 @@
{% block scripts %}
<script>
// Event data from API - sécuriser le parsing
let eventData = null;
// Event data from API - corriger le double-encodage
window.eventData = null;
try {
const rawData = '{{ event_data|tojson|safe }}';
console.log('🔍 Données brutes reçues:', rawData);
eventData = JSON.parse(rawData);
console.log('✅ Données événement parsées:', eventData);
// Rendre les données disponibles globalement
window.eventData = eventData;
// Utiliser directement les données JSON sans double-encodage
window.eventData = {{ event_data|safe }};
console.log('✅ Données événement chargées:', window.eventData);
} catch (error) {
console.error('❌ Erreur de parsing des données événement:', error);
console.error('Données brutes:', '{{ event_data|tojson|safe }}');
console.error('❌ Erreur de chargement des données événement:', error);
console.error('Données brutes JSON:', '{{ event_data|safe }}');
// Afficher une erreur à l'utilisateur
document.addEventListener('DOMContentLoaded', function() {
@ -104,7 +102,7 @@
resultElement.innerHTML = `
<div style="color: #dc3545; padding: 15px; background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 4px;">
<i class="fas fa-exclamation-triangle"></i>
<strong>Erreur de chargement:</strong> Impossible de parser les données de l'événement.
<strong>Erreur de chargement:</strong> Impossible de charger les données de l'événement.
<br><small>Erreur technique: ${error.message}</small>
</div>
`;

View file

@ -0,0 +1,177 @@
{% extends "layout.html" %}
{% block title %}Statistiques des propriétés - OpenEventDatabase{% endblock %}
{% block css %}
<style>
.stats-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.stats-summary {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
border-left: 4px solid #0078ff;
}
.stats-table {
width: 100%;
border-collapse: collapse;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-radius: 8px;
overflow: hidden;
}
.stats-table th,
.stats-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.stats-table th {
background-color: #0078ff;
color: white;
font-weight: bold;
}
.stats-table tbody tr:hover {
background-color: #f8f9fa;
}
.stats-table tbody tr:nth-child(even) {
background-color: #fafafa;
}
.percentage {
color: #666;
font-size: 0.9em;
}
.property-name {
font-family: monospace;
background-color: #f1f3f4;
padding: 2px 4px;
border-radius: 3px;
}
.occurrence-count {
font-weight: bold;
color: #0078ff;
}
.nav-breadcrumb {
margin-bottom: 20px;
color: #666;
}
.nav-breadcrumb a {
color: #0078ff;
text-decoration: none;
}
.nav-breadcrumb a:hover {
text-decoration: underline;
}
.refresh-info {
font-size: 0.9em;
color: #666;
margin-bottom: 20px;
text-align: right;
}
</style>
{% endblock %}
{% block header %}Statistiques des propriétés des événements{% endblock %}
{% block content %}
<div class="stats-container">
<div class="nav-breadcrumb">
<a href="/">Accueil</a>
<a href="/demo">Demo</a>
Statistiques des propriétés
</div>
<div class="refresh-info">
Basé sur les {{ total_events }} derniers événements de la base de données
</div>
<div class="stats-summary">
<h2>Résumé</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px;">
<div>
<strong>Nombre total d'événements analysés:</strong><br>
<span style="font-size: 1.5em; color: #0078ff;">{{ total_events }}</span>
</div>
<div>
<strong>Propriétés uniques trouvées:</strong><br>
<span style="font-size: 1.5em; color: #28a745;">{{ unique_properties }}</span>
</div>
<div>
<strong>Propriété la plus commune:</strong><br>
{% if property_stats %}
<span class="property-name">{{ property_stats[0][0] }}</span><br>
<small>({{ property_stats[0][1] }} occurrences)</small>
{% else %}
<span>Aucune donnée</span>
{% endif %}
</div>
</div>
</div>
{% if property_stats %}
<table class="stats-table">
<thead>
<tr>
<th style="width: 50px;">Rang</th>
<th>Nom de la propriété</th>
<th style="width: 120px;">Occurrences</th>
<th style="width: 100px;">Pourcentage</th>
</tr>
</thead>
<tbody>
{% for property_name, count in property_stats %}
<tr>
<td>{{ loop.index }}</td>
<td><span class="property-name">{{ property_name }}</span></td>
<td class="occurrence-count">{{ count }}</td>
<td class="percentage">{{ "%.1f"|format((count / total_events * 100)) }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div style="text-align: center; padding: 40px; color: #666;">
<h3>Aucune donnée disponible</h3>
<p>Impossible de récupérer les statistiques des propriétés pour le moment.</p>
</div>
{% endif %}
<div style="margin-top: 30px; text-align: center;">
<a href="/demo" style="display: inline-block; padding: 10px 20px; background-color: #0078ff; color: white; text-decoration: none; border-radius: 5px;">
← Retour à la démo
</a>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
// Auto-refresh every 5 minutes
setTimeout(function() {
window.location.reload();
}, 5 * 60 * 1000);
// Add click handler to property names for future enhancement
document.querySelectorAll('.property-name').forEach(function(element) {
element.style.cursor = 'pointer';
element.title = 'Propriété: ' + element.textContent;
});
</script>
{% endblock %}