cocorahs
This commit is contained in:
139
covid.py
139
covid.py
@@ -11,6 +11,25 @@ import datetime
|
||||
from html import escape
|
||||
from tabulate import tabulate
|
||||
|
||||
def clean_text(text):
|
||||
"""Clean text by removing problematic characters that cause encoding issues."""
|
||||
if text is None:
|
||||
return ''
|
||||
|
||||
# Convert to string if not already
|
||||
text = str(text)
|
||||
|
||||
# Remove the problematic 'Â' character that appears with degree symbols
|
||||
text = text.replace('\u00a0', ' ') # Non-breaking space to regular space
|
||||
text = text.replace('\xc2', '') # Remove standalone  character
|
||||
text = text.replace('°', '°') # Fix degree symbol encoding issue
|
||||
text = text.replace('°F', '°F') # Fix degree F encoding issue
|
||||
text = text.replace('°C', '°C') # Fix degree C encoding issue
|
||||
text = text.replace('\u00a0', ' ') # Remove non-breaking spaces
|
||||
text = text.encode('utf-8', errors='ignore').decode('utf-8') # Handle other encoding issues
|
||||
|
||||
return text
|
||||
|
||||
allobs = []
|
||||
|
||||
states = ['wv', 'oh', 'va', 'ky']
|
||||
@@ -25,7 +44,7 @@ try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status() # Check for HTTP errors
|
||||
data = xmltodict.parse(response.content.decode('utf-8')) # Explicitly decode as UTF-8
|
||||
|
||||
|
||||
try:
|
||||
daily_reports = data.get('Cocorahs', {}).get('DailyPrecipReports')
|
||||
if daily_reports is None:
|
||||
@@ -55,13 +74,13 @@ try:
|
||||
finalobs = []
|
||||
for obs in allobs:
|
||||
tempob = [
|
||||
obs.get('DateTimeStamp', ''),
|
||||
obs.get('StationNumber', ''),
|
||||
obs.get('StationName', ''),
|
||||
obs.get('TotalPrecipAmt', ''),
|
||||
obs.get('NewSnowDepth', ''),
|
||||
obs.get('TotalSnowDepth', ''),
|
||||
obs.get('Notes', '')
|
||||
clean_text(obs.get('DateTimeStamp', '')),
|
||||
clean_text(obs.get('StationNumber', '')),
|
||||
clean_text(obs.get('StationName', '')),
|
||||
clean_text(obs.get('TotalPrecipAmt', '')),
|
||||
clean_text(obs.get('NewSnowDepth', '')),
|
||||
clean_text(obs.get('TotalSnowDepth', '')),
|
||||
clean_text(obs.get('Notes', ''))
|
||||
]
|
||||
finalobs.append(tempob)
|
||||
|
||||
@@ -78,27 +97,30 @@ try:
|
||||
html_content = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Cocorahs Weather Data - """ + datewanted + """</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||
th { background-color: #f2f2f2; font-weight: bold; }
|
||||
th { background-color: #f2f2f2; font-weight: bold; cursor: pointer; }
|
||||
th:hover { background-color: #e0e0e0; }
|
||||
tr:nth-child(even) { background-color: #f9f9f9; }
|
||||
.sort-arrow { margin-left: 5px; font-size: 0.8em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Cocorahs Weather Data - """ + datewanted + """</h1>
|
||||
<table>
|
||||
<table id="dataTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date/Time of Ob (Z)</th>
|
||||
<th>Station Number</th>
|
||||
<th>Station Name</th>
|
||||
<th>New Precip</th>
|
||||
<th>New Snow</th>
|
||||
<th>Snow Depth</th>
|
||||
<th>Comments</th>
|
||||
<th onclick="sortTable(0)" title="Click to sort by Date/Time">Date/Time of Ob (Z)<span class="sort-arrow"></span></th>
|
||||
<th onclick="sortTable(1)" title="Click to sort by Station Number">Station Number<span class="sort-arrow"></span></th>
|
||||
<th onclick="sortTable(2)" title="Click to sort by Station Name">Station Name<span class="sort-arrow"></span></th>
|
||||
<th onclick="sortTable(3)" title="Click to sort by New Precip">New Precip<span class="sort-arrow"></span></th>
|
||||
<th onclick="sortTable(4)" title="Click to sort by New Snow">New Snow<span class="sort-arrow"></span></th>
|
||||
<th onclick="sortTable(5)" title="Click to sort by Snow Depth">Snow Depth<span class="sort-arrow"></span></th>
|
||||
<th onclick="sortTable(6)" title="Click to sort by Comments">Comments<span class="sort-arrow"></span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -112,6 +134,89 @@ try:
|
||||
|
||||
html_content += """ </tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
let sortDirection = Array(7).fill(true); // true for ascending, false for descending for 7 columns
|
||||
|
||||
function sortTable(columnIndex) {
|
||||
const table = document.getElementById('dataTable');
|
||||
const tbody = table.querySelector('tbody');
|
||||
const rows = Array.from(tbody.rows);
|
||||
|
||||
// Determine the data type for this column
|
||||
let isNumeric = [3, 4, 5].includes(columnIndex); // New Precip, New Snow, Snow Depth are numeric
|
||||
let isDateTime = columnIndex === 0; // Date/Time is special
|
||||
|
||||
// Toggle sort direction
|
||||
sortDirection[columnIndex] = !sortDirection[columnIndex];
|
||||
const direction = sortDirection[columnIndex];
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const aCell = a.cells[columnIndex].textContent.trim();
|
||||
const bCell = b.cells[columnIndex].textContent.trim();
|
||||
|
||||
let comparison = 0;
|
||||
|
||||
if (isDateTime) {
|
||||
// Parse date for comparison (assuming format like: MM/DD/YYYY HH:MM:SS)
|
||||
const dateA = parseDateTime(aCell);
|
||||
const dateB = parseDateTime(bCell);
|
||||
comparison = dateA - dateB;
|
||||
} else if (isNumeric) {
|
||||
// Convert to numeric values for comparison
|
||||
const numA = parseFloat(aCell) || 0;
|
||||
const numB = parseFloat(bCell) || 0;
|
||||
comparison = numA - numB;
|
||||
} else {
|
||||
// Text comparison for A-Z/Z-A
|
||||
comparison = aCell.localeCompare(bCell, undefined, {numeric: true, sensitivity: 'base'});
|
||||
}
|
||||
|
||||
return direction ? comparison : -comparison;
|
||||
});
|
||||
|
||||
// Reorder the rows in the table
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
|
||||
// Update sort arrows
|
||||
updateSortArrows(columnIndex, direction);
|
||||
}
|
||||
|
||||
function parseDateTime(dateString) {
|
||||
// Handle various date formats, default to string if not parseable
|
||||
if (!dateString || dateString.trim() === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try to parse date in common formats
|
||||
let date = new Date(dateString);
|
||||
if (isNaN(date)) {
|
||||
// Try alternative parsing if the default doesn't work
|
||||
const parts = dateString.replace(',', '').split(/[\s\/:]+/);
|
||||
if (parts.length >= 3) {
|
||||
// Assuming MM/DD/YYYY format
|
||||
date = new Date(parts[2], parts[0] - 1, parts[1]);
|
||||
}
|
||||
}
|
||||
return date.getTime ? date.getTime() : 0;
|
||||
}
|
||||
|
||||
function updateSortArrows(columnIndex, direction) {
|
||||
// Clear all arrows first
|
||||
const allArrows = document.querySelectorAll('.sort-arrow');
|
||||
allArrows.forEach(arrow => arrow.textContent = '');
|
||||
|
||||
// Set arrow for the current column
|
||||
const currentArrow = document.querySelectorAll('.sort-arrow')[columnIndex];
|
||||
currentArrow.textContent = direction ? ' ▲' : ' ▼';
|
||||
}
|
||||
|
||||
// Initialize arrows on load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const arrows = document.querySelectorAll('.sort-arrow');
|
||||
arrows.forEach(arrow => arrow.textContent = '');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user