oedb-backend/oedb/resources/demo.py
2025-09-27 00:39:18 +02:00

467 lines
No EOL
18 KiB
Python

"""
Demo resource for the OpenEventDatabase.
This module imports and re-exports the demo resources from the demo package.
"""
import falcon
import requests
import json
import os
from collections import defaultdict
from oedb.utils.logging import logger
from oedb.utils.db import load_env_from_file
from oedb.resources.demo import demo_main, demo_traffic, demo_view_events
class DemoResource:
"""
Resource for the demo endpoint.
Handles the /demo endpoint and related demo pages.
"""
def __init__(self):
"""
Initialize the resource with a Jinja2 environment.
"""
# Set up Jinja2 environment
import jinja2
import os
template_dir = os.path.join(os.path.dirname(__file__), 'demo', 'templates')
self.jinja_env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_dir),
autoescape=jinja2.select_autoescape(['html', 'xml'])
)
def on_get_edit(self, req, resp, id=None):
"""
Handle GET requests to the /demo/edit endpoint.
Returns an HTML page with a form for editing an existing event.
Args:
req: The request object.
resp: The response object.
id: The event ID to edit.
"""
logger.info(f"Processing GET request to /demo/edit for event ID: {id}")
if id is None:
resp.status = falcon.HTTP_400
resp.text = "Event ID is required"
return
try:
# Set content type to HTML
resp.content_type = 'text/html'
# Fetch the event data from the API
response = requests.get(f'https://api.openeventdatabase.org/event/{id}')
if response.status_code != 200:
resp.status = falcon.HTTP_404
resp.text = f"Event with ID {id} not found"
return
event_data = response.json()
# Render the template with the event data
template = self.jinja_env.get_template('edit.html')
html = template.render(
id=id,
event_data=event_data
)
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success(f"Successfully processed GET request to /demo/edit for event ID: {id}")
except Exception as e:
logger.error(f"Error processing GET request to /demo/edit: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
def on_get(self, req, resp):
"""
Handle GET requests to the /demo endpoint.
Delegates to the demo_main resource.
Args:
req: The request object.
resp: The response object.
"""
return demo_main.on_get(req, resp)
def on_get_by_what(self, req, resp):
"""
Handle GET requests to the /demo/by-what endpoint.
Returns an HTML page with links to events organized by their "what" type.
Args:
req: The request object.
resp: The response object.
"""
logger.info("Processing GET request to /demo/by-what")
try:
# Set content type to HTML
resp.content_type = 'text/html'
# Fetch events from the API
try:
response = requests.get('/event?limit=1000')
if response.status_code == 200 and response.text:
events_data = response.json()
else:
logger.error(f"Error fetching events: Status code {response.status_code}, Response: {response.text}")
events_data = {"features": []}
except json.JSONDecodeError as e:
logger.error(f"Error parsing JSON response: {e}")
events_data = {"features": []}
except Exception as e:
logger.error(f"Error fetching events: {e}")
events_data = {"features": []}
# Group events by "what" type
events_by_what = defaultdict(list)
if events_data.get('features'):
for feature in events_data['features']:
properties = feature.get('properties', {})
what = properties.get('what', 'Unknown')
events_by_what[what].append({
'id': properties.get('id'),
'label': properties.get('label', 'Unnamed Event'),
'coordinates': feature.get('geometry', {}).get('coordinates', [0, 0])
})
# Load and render the template with the appropriate variables
template = self.jinja_env.get_template('by_what.html')
# Sort event types alphabetically if we have events
sorted_what_types = sorted(events_by_what.keys()) if events_by_what else []
html = template.render(
events_by_what=events_by_what,
sorted_what_types=sorted_what_types
)
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /demo/by-what")
except Exception as e:
logger.error(f"Error processing GET request to /demo/by-what: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
def on_get_search(self, req, resp):
"""
Handle GET requests to the /demo/search endpoint.
Returns an HTML page with a form for searching events and displaying results.
Args:
req: The request object.
resp: The response object.
"""
logger.info("Processing GET request to /demo/search")
try:
# Set content type to HTML
resp.content_type = 'text/html'
# Render the template
template = self.jinja_env.get_template('search.html')
html = template.render()
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /demo/search")
except Exception as e:
logger.error(f"Error processing GET request to /demo/search: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
def on_get_map_by_what(self, req, resp):
"""
Handle GET requests to the /demo/map-by-what endpoint.
Returns an HTML page with a MapLibre map showing events filtered by "what" type.
Args:
req: The request object.
resp: The response object.
"""
logger.info("Processing GET request to /demo/map-by-what")
try:
# Set content type to HTML
resp.content_type = 'text/html'
# Render the template
template = self.jinja_env.get_template('map_by_what.html')
html = template.render()
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /demo/map-by-what")
except Exception as e:
logger.error(f"Error processing GET request to /demo/map-by-what: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
def on_get_map_by_what_type(self, req, resp, event_type):
"""
Handle GET requests to the /demo/map-by-what/{type} endpoint.
Returns an HTML page with a MapLibre map showing events of a specific type,
colored by temporality (past/present/future) with a detailed table.
Args:
req: The request object.
resp: The response object.
event_type: The event type to filter by.
"""
logger.info(f"Processing GET request to /demo/map-by-what/{event_type}")
try:
# Set content type to HTML
resp.content_type = 'text/html'
# Render the template with the event type
template = self.jinja_env.get_template('map_by_what_type.html')
html = template.render(
event_type=event_type,
event_type_label=event_type.replace('_', ' ').title()
)
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success(f"Successfully processed GET request to /demo/map-by-what/{event_type}")
except Exception as e:
logger.error(f"Error processing GET request to /demo/map-by-what/{event_type}: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
events_by_what = defaultdict(list)
if events_data.get('features'):
for feature in events_data['features']:
properties = feature.get('properties', {})
what = properties.get('what', 'Unknown')
events_by_what[what].append({
'id': properties.get('id'),
'label': properties.get('label', 'Unnamed Event'),
'coordinates': feature.get('geometry', {}).get('coordinates', [0, 0])
})
# Create HTML response
html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Events by Type - OpenEventDatabase</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 { color: #333; }
h2 {
color: #0078ff;
margin-top: 30px;
padding-bottom: 5px;
border-bottom: 1px solid #eee;
}
ul { padding-left: 20px; }
li { margin-bottom: 8px; }
a { color: #0078ff; text-decoration: none; }
a:hover { text-decoration: underline; }
.nav {
background-color: #f5f5f5;
padding: 10px;
border-radius: 5px;
margin-bottom: 20px;
}
.nav a {
margin-right: 15px;
}
.event-count {
color: #666;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="nav">
<a href="/">Home</a>
<a href="/demo">Demo Map</a>
<a href="/demo/map-by-what">Map by Event Type</a>
</div>
<h1>Events by Type</h1>
<p>This page lists all events from the OpenEventDatabase organized by their type.</p>
"""
# Add event types and their events
if events_by_what:
# Sort event types alphabetically
sorted_what_types = sorted(events_by_what.keys())
# Add quick navigation
html += "<h2>Quick Navigation</h2><ul>"
for what_type in sorted_what_types:
event_count = len(events_by_what[what_type])
html += f'<li><a href="#what-{what_type.replace(" ", "-")}">{what_type}</a> <span class="event-count">({event_count} events)</span></li>'
html += "</ul>"
# Add sections for each event type
for what_type in sorted_what_types:
events = events_by_what[what_type]
html += f'<h2 id="what-{what_type.replace(" ", "-")}">{what_type} <span class="event-count">({len(events)} events)</span></h2>'
html += "<ul>"
# Sort events by label
sorted_events = sorted(events, key=lambda x: x.get('label', ''))
for event in sorted_events:
event_id = event.get('id')
event_label = event.get('label', 'Unnamed Event')
coordinates = event.get('coordinates', [0, 0])
html += f'<li><a href="/event/{event_id}" >{event_label}</a> '
html += f'<small>[<a href="https://www.openstreetmap.org/?mlat={coordinates[1]}&mlon={coordinates[0]}&zoom=15" >map</a>]</small></li>'
html += "</ul>"
else:
html += "<p>No events found in the database.</p>"
html += """
</body>
</html>
"""
# Set the response body and status
resp.text = html
resp.status = falcon.HTTP_200
logger.success("Successfully processed GET request to /demo/by-what")
except Exception as e:
logger.error(f"Error processing GET request to /demo/by-what: {e}")
resp.status = falcon.HTTP_500
resp.text = f"Error: {str(e)}"
def on_get_traffic(self, req, resp):
"""
Handle GET requests to the /demo/traffic endpoint.
Delegates to the demo_traffic resource.
Args:
req: The request object.
resp: The response object.
"""
return demo_traffic.on_get(req, resp)
def on_get_view_events(self, req, resp):
"""
Handle GET requests to the /demo/view-events endpoint.
Delegates to the demo_view_events resource.
Args:
req: The request object.
resp: The response object.
"""
return demo_view_events.on_get(req, resp)
def on_get_by_id(self, req, resp, id):
"""
Handle GET requests to /demo/by_id/{id}.
Show a map with the event location and a table of its properties.
"""
import requests
import json
logger.info(f"Processing GET request to /demo/by_id/{id}")
try:
resp.content_type = 'text/html'
r = requests.get(f"https://api.openeventdatabase.org/event/{id}")
r.raise_for_status()
feature = r.json()
# Load and render the template
template = self.jinja_env.get_template('event_details.html')
html = template.render(
id=id,
feature_json=json.dumps(feature),
properties=feature.get('properties') or {}
)
resp.text = html
resp.status = falcon.HTTP_200
logger.success(f"Successfully processed GET request to /demo/by_id/{id}")
except Exception as e:
logger.error(f"Error processing GET request to /demo/by_id/{id}: {e}")
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()