intermediatry commit
styles.css - complete generate_earthquakes.py - partially done geonet_earthquakes_list.html - dynamically generated by generate_earthquakes.py
This commit is contained in:
143
generate_earthquakes.py
Normal file
143
generate_earthquakes.py
Normal file
@@ -0,0 +1,143 @@
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
import pytz
|
||||
|
||||
def parse_geonet_title(title):
|
||||
match = re.search(r'\d+\skm\s(north|south|west|east|north-east|north-west|south-east|south-west|of)?\s*(.*)', title)
|
||||
if match:
|
||||
location = match.group(2).strip()
|
||||
location = location.replace('region', '').strip()
|
||||
return location if location else "Unknown Location"
|
||||
|
||||
match = re.match(r'M\s\d+\.?\d*,\s*(.*)', title)
|
||||
if match:
|
||||
location = match.group(1).strip()
|
||||
location = location.replace('region', '').strip()
|
||||
return location if location else "Unknown Location"
|
||||
|
||||
return title
|
||||
|
||||
def get_earthquakes(min_mmi=8, limit=20, from_today_only=True):
|
||||
url = "https://api.geonet.org.nz/quakes/services/quake/"
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
all_quakes = []
|
||||
|
||||
utc_timezone = pytz.utc
|
||||
nzt_timezone = pytz.timezone('Pacific/Auckland')
|
||||
|
||||
current_nzt_date = datetime.now(nzt_timezone).date()
|
||||
|
||||
for feature in data.get('features', []):
|
||||
props = feature.get('properties', {})
|
||||
mmi = props.get('mmi')
|
||||
time_utc_ms = props.get('time')
|
||||
status = props.get('status', 'latest')
|
||||
quake_id = feature.get('id')
|
||||
|
||||
if time_utc_ms is None:
|
||||
continue
|
||||
|
||||
dt_object_utc_aware = utc_timezone.localize(datetime.fromtimestamp(time_utc_ms / 1000))
|
||||
dt_object_nzt = dt_object_utc_aware.astimezone(nzt_timezone)
|
||||
|
||||
if from_today_only:
|
||||
if dt_object_nzt.date() != current_nzt_date:
|
||||
continue
|
||||
|
||||
if status != 'deleted':
|
||||
if mmi is None or mmi < min_mmi:
|
||||
continue
|
||||
|
||||
title = props.get('title', 'Unknown Event')
|
||||
time_str = dt_object_nzt.strftime("%d - %b - %Y %I:%M %p %z")
|
||||
location = parse_geonet_title(title)
|
||||
|
||||
all_quakes.append({
|
||||
'mmi': mmi,
|
||||
'magnitude': props.get('magnitude'),
|
||||
'depth': props.get('depth'),
|
||||
'location': location,
|
||||
'time': time_str,
|
||||
'id': quake_id,
|
||||
'utc_timestamp': time_utc_ms,
|
||||
'status': status
|
||||
})
|
||||
|
||||
all_quakes.sort(key=lambda x: x['utc_timestamp'], reverse=True)
|
||||
return all_quakes[:limit]
|
||||
|
||||
except requests.exceptioins.RequestException as e:
|
||||
print(f"Error fetching data from Geonet API: {e}")
|
||||
return []
|
||||
|
||||
def generate_html(quakes, output_file="geonet_earthquakes.html", css_file="style.css"):
|
||||
html_content = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Latest Earthquakes NZ</title>
|
||||
<link rel="stylesheet" href="{css_file}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="earthquake-list">
|
||||
"""
|
||||
|
||||
if not quakes:
|
||||
html_content += """
|
||||
<div class="earthquake-item">
|
||||
<div class="mmi-box" style="background-color: #444; color: white;">--</div>
|
||||
"""
|
||||
else:
|
||||
for quake in quakes:
|
||||
deleted_class = " deleted-item" if quake['status'] == 'deleted' else ""
|
||||
|
||||
mmi_box_classes = ""
|
||||
mmi_box_content = ""
|
||||
|
||||
if quake['status'] == 'deleted':
|
||||
mmi_box_classes = "deleted-marker"
|
||||
mmi_box_content = "X"
|
||||
else:
|
||||
mmi_class_value = min(quake['mmi'] if quake['mmi'] is not None else 0, 11)
|
||||
if mmi_class_value >= 8:
|
||||
mmi_box_classes = "mmi-8"
|
||||
else:
|
||||
mmi_box_classes = f"mmi-{mmi_class_value}"
|
||||
mmi_box_content = str(quake['mmi']) if quake['mmi'] is not None else '--'
|
||||
|
||||
magnitude_display = f"M{quake['magnitude']:.1f}" if quake['magnitude'] is not None else '--'
|
||||
depth_display = f"{quake['depth']}km" if quake['depth'] is not None else '--km'
|
||||
location_display = quake['location'] if quake['location'] else 'Unknown Location'
|
||||
time_display = quake['time'] if quake['time'] else 'Unknown Time'
|
||||
status_display = quake['status'].capitalize() if quake['status'] else '--'
|
||||
id_display = quake['id'] if quake['id'] else '--'
|
||||
|
||||
html_content += f"""
|
||||
<div class="earthquake-item{deleted_class}">
|
||||
<div class="mmi-box {mmi_box_clases}">{mmi_box_content}</div>
|
||||
<div class="detail-label status-line">Status: <span style="font-weight: normal; color: white;">{status_display}</span></div>
|
||||
<div class="detail-label mag-depth">
|
||||
<span>Magnitude: <span style="font-weight: normal; color: white;">{magnitude_display}</span></span>
|
||||
<span>Depth: <span style="font-weight: normal; color: white;">{depth_display}</span></span>
|
||||
</div>
|
||||
<div class="detail-label location">Where: <span style="font-weight: normal; color: white;">{location_display}</span></div>
|
||||
<div class="detail-label time">When: <span style="font-weight: normal; color: white;">{time_display}</span></div>
|
||||
<div class="detail-label id-line">Quake ID: <span style="font-weight: normal; color: white;">{id_display}</span></div>
|
||||
"""
|
||||
|
||||
html_content += """
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
0
geonet_earhtquakes_list.html
Normal file
0
geonet_earhtquakes_list.html
Normal file
109
style.css
Normal file
109
style.css
Normal file
@@ -0,0 +1,109 @@
|
||||
/* import for google fonts */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap'); /* Roboto */
|
||||
@import url('https://fonts.googleapis.com/css2?familt=Source+Code+Pro&display=swap'); /* Monospace */
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* fallback if imports fail */
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
width: 125px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.earthquake-item {
|
||||
display: grid;
|
||||
grid-template-columns: 20px 1fr;
|
||||
grid-template-rows: auto auto auto auto auto auto;
|
||||
gap: 2px 5px;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
padding: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 4px;
|
||||
box-shaddow: 0 1px 3px rgba(0, 0, 0, 0.4);
|
||||
font-size: 0.85em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.earthquake-item.deleted-item {
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.earthquake-item.deleted-item .detail-label span{
|
||||
text-decoration: line-through 2px red;
|
||||
}
|
||||
|
||||
.earthquake-item.deleted-item .status-line,
|
||||
.earhtquake-item.delted-item .status-line span{
|
||||
text-decoration: none !important;
|
||||
color: #bbb !important;
|
||||
}
|
||||
|
||||
.earthquake-item.deleted-item .status-line span{
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.mmi-box {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / span 4;
|
||||
color: black;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 3px;
|
||||
width: 20px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.mmi-box.deleted-marker {
|
||||
background-color: transparent;
|
||||
color: red;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
font-family: Arial, sans-serif;
|
||||
text-shaddow: 1px 1px 2px rgba(192, 192, 192, 0.7);
|
||||
}
|
||||
|
||||
/* geonet colors */
|
||||
.mmi-1: {background-color: #fff7f3;} /* Unnoticable */
|
||||
.mmi-2: {background-color: #feedde;} /* Unnoticable */
|
||||
.mmi-3: {background-color: #fdd0a2;} /* weak */
|
||||
.mmi-4: {background-color: #fdae6b;} /* Light */
|
||||
.mmi-5: {background-color: #fd8d3c;} /* moderate */
|
||||
.mmi-6: {background-color: #f16913;} /* strong */
|
||||
.mmi-7: {background-color: #f03b20;} /* servere */
|
||||
|
||||
/* .mmi 8-11 mapped to the same color */
|
||||
.mmi-8: {background-color: #bd0026;} /* Extreme */
|
||||
|
||||
.detail-label {
|
||||
grid-column: 2 / 3;
|
||||
font-weight: bold;
|
||||
color: #bbb;
|
||||
white-space: nowrap;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.detail-label span{
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-weight: normal;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-line {grid-row: 1 / 2;}
|
||||
.mag-depth {grid-row: 2 / 3; display: flex; justify-content: space-between;}
|
||||
.location {grid-row: 3 / 4;}
|
||||
.time {grid-row: 4 / 5;}
|
||||
.id-line {grid-row: 5 / 6;}
|
Reference in New Issue
Block a user